#include "FSDSAX2Parser.h"
#include "FeatureService.h"
#include "ResourceService.h"
#include "Services/Feature/CreateFeatureSource.h"
#include "Services/Feature/FdoConnectionUtil.h"
#include "Services/Feature/FeatureDefs.h"
#include "Services/Feature/FeatureServiceCache.h"
#include "Services/Feature/RdbmsFeatureSourceParams.h"
#include "ServiceFactory.h"
#include "Fdo.h"
#include "FdoExpressionEngine.h"
#include "FdoExpressionEngineCopyFilter.h"
#include "PlatformBase.h"
#include "Services/Feature/FeatureUtil.h"
#include "FeatureReader.h"
#include "DataReader.h"
#include "SqlReader.h"

static std::map<FdoThreadCapability, std::string>          s_FdoThreadCapability;
static std::map<FdoSpatialContextExtentType, std::string>  s_FdoSpatialContextExtentType;
static std::map<FdoClassType, std::string>                 s_FdoClassType;
static std::map<FdoDataType,  std::string>                 s_FdoDataType;
static std::map<FdoPropertyType,  std::string>             s_FdoPropertyTypeAsString;
static std::map<FdoFunctionCategoryType,  std::string>     s_FdoFunctionCategoryType;
static std::map<FdoCommandType,  std::string>              s_FdoCommandType;
static std::map<FdoConditionType,  std::string>            s_FdoConditionType;
static std::map<FdoSpatialOperations,  std::string>        s_FdoSpatialOperations;
static std::map<FdoDistanceOperations,  std::string>       s_FdoDistanceOperations;
static std::map<FdoExpressionType,  std::string>           s_FdoExpressionType;
static std::map<FdoGeometryType,  std::string>             s_FdoGeometryType;
static std::map<FdoGeometryComponentType,  std::string>    s_FdoGeometryComponentType;

bool MgdFeatureService::m_isInitialized = MgdFeatureService::Initialize();

MgdFeatureService::MgdFeatureService() 
: MgFeatureService()
{ 
	
}

MgdFeatureService::~MgdFeatureService() 
{ 
	
}

bool MgdFeatureService::Initialize()
{
    // Thread Capability
    s_FdoThreadCapability[FdoThreadCapability_SingleThreaded]           = "SingleThreaded";
    s_FdoThreadCapability[FdoThreadCapability_PerConnectionThreaded]    = "PerConnectionThreaded";
    s_FdoThreadCapability[FdoThreadCapability_PerCommandThreaded]       = "PerCommandThreaded";
    s_FdoThreadCapability[FdoThreadCapability_MultiThreaded]            = "MultiThreaded";

    // Spatial Extent type
    s_FdoSpatialContextExtentType[FdoSpatialContextExtentType_Static]   = "Static";
    s_FdoSpatialContextExtentType[FdoSpatialContextExtentType_Dynamic]  = "Dynamic";

    // Class Types
    s_FdoClassType[FdoClassType_Class]                  = "Class";
    s_FdoClassType[FdoClassType_FeatureClass]           = "FeatureClass";
    s_FdoClassType[FdoClassType_NetworkClass]           = "NetworkClass";
    s_FdoClassType[FdoClassType_NetworkLayerClass]      = "NetworkLayerClass";
    s_FdoClassType[FdoClassType_NetworkNodeClass]       = "NetworkNodeClass";

    // Data types
    s_FdoDataType[FdoDataType_Boolean]     = "Boolean";
    s_FdoDataType[FdoDataType_Byte]        = "Byte";
    s_FdoDataType[FdoDataType_DateTime]    = "DateTime";
    // Implementation Note:  FdoDataType_Decimal is currently mapped to MgPropertyType::Double.
    // An MgDecimalProperty class should be implemented in a future release.
    s_FdoDataType[FdoDataType_Decimal]     = "Double";
    s_FdoDataType[FdoDataType_Double]      = "Double";
    s_FdoDataType[FdoDataType_Int16]       = "Int16";
    s_FdoDataType[FdoDataType_Int32]       = "Int32";
    s_FdoDataType[FdoDataType_Int64]       = "Int64";
    s_FdoDataType[FdoDataType_Single]      = "Single";
    s_FdoDataType[FdoDataType_String]      = "String";
    s_FdoDataType[FdoDataType_BLOB]        = "BLOB";
    s_FdoDataType[FdoDataType_CLOB]        = "CLOB";

    s_FdoPropertyTypeAsString[FdoPropertyType_DataProperty]     = "Data";
    s_FdoPropertyTypeAsString[FdoPropertyType_GeometricProperty]        = "Geometry";
    s_FdoPropertyTypeAsString[FdoPropertyType_ObjectProperty]        = "Object";
    s_FdoPropertyTypeAsString[FdoPropertyType_AssociationProperty]        = "Association";
    s_FdoPropertyTypeAsString[FdoPropertyType_RasterProperty]        = "Raster";

    s_FdoFunctionCategoryType[FdoFunctionCategoryType_Aggregate] = "Aggregate";
    s_FdoFunctionCategoryType[FdoFunctionCategoryType_Conversion] = "Conversion";
    s_FdoFunctionCategoryType[FdoFunctionCategoryType_Custom] = "Custom";
    s_FdoFunctionCategoryType[FdoFunctionCategoryType_Date] = "Date";
    s_FdoFunctionCategoryType[FdoFunctionCategoryType_Geometry] = "Geometry";
    s_FdoFunctionCategoryType[FdoFunctionCategoryType_Math] = "Math";
    s_FdoFunctionCategoryType[FdoFunctionCategoryType_Numeric] = "Numeric";
    s_FdoFunctionCategoryType[FdoFunctionCategoryType_String] = "String";
    s_FdoFunctionCategoryType[FdoFunctionCategoryType_Unspecified] = "Unspecified";

    // Commands
    s_FdoCommandType[FdoCommandType_Select]                             = "Select";
    s_FdoCommandType[FdoCommandType_Insert]                             = "Insert";
    s_FdoCommandType[FdoCommandType_Delete]                             = "Delete";
    s_FdoCommandType[FdoCommandType_Update]                             = "Update";
    s_FdoCommandType[FdoCommandType_DescribeSchema]                     = "DescribeSchema";
    s_FdoCommandType[FdoCommandType_DescribeSchemaMapping]              = "DescribeSchemaMapping";
    s_FdoCommandType[FdoCommandType_ApplySchema]                        = "ApplySchema";
    s_FdoCommandType[FdoCommandType_DestroySchema]                      = "DestroySchema";
    s_FdoCommandType[FdoCommandType_ActivateSpatialContext]             = "ActivateSpatialContext";
    s_FdoCommandType[FdoCommandType_CreateSpatialContext]               = "CreateSpatialContext";
    s_FdoCommandType[FdoCommandType_DestroySpatialContext]              = "DestroySpatialContext";
    s_FdoCommandType[FdoCommandType_GetSpatialContexts]                 = "GetSpatialContexts";
    s_FdoCommandType[FdoCommandType_CreateMeasureUnit]                  = "CreateMeasureUnit";
    s_FdoCommandType[FdoCommandType_DestroyMeasureUnit]                 = "DestroyMeasureUnit";
    s_FdoCommandType[FdoCommandType_GetMeasureUnits]                    = "GetMeasureUnits";
    s_FdoCommandType[FdoCommandType_SQLCommand]                         = "SQLCommand";
    s_FdoCommandType[FdoCommandType_AcquireLock]                        = "AcquireLock";
    s_FdoCommandType[FdoCommandType_GetLockInfo]                        = "GetLockInfo";
    s_FdoCommandType[FdoCommandType_GetLockedObjects]                   = "GetLockedObjects";
    s_FdoCommandType[FdoCommandType_GetLockOwners]                      = "GetLockOwners";
    s_FdoCommandType[FdoCommandType_ReleaseLock]                        = "ReleaseLock";
    s_FdoCommandType[FdoCommandType_ActivateLongTransaction]            = "ActivateLongTransaction";
    s_FdoCommandType[FdoCommandType_DeactivateLongTransaction]          = "DeactivateLongTransaction";
    s_FdoCommandType[FdoCommandType_CommitLongTransaction]              = "CommitLongTransaction";
    s_FdoCommandType[FdoCommandType_CreateLongTransaction]              = "CreateLongTransaction";
    s_FdoCommandType[FdoCommandType_GetLongTransactions]                = "GetLongTransactions";
    s_FdoCommandType[FdoCommandType_FreezeLongTransaction]              = "FreezeLongTransaction";
    s_FdoCommandType[FdoCommandType_RollbackLongTransaction]            = "RollbackLongTransaction";
    s_FdoCommandType[FdoCommandType_ActivateLongTransactionCheckpoint]  = "ActivateLongTransactionCheckpoint";
    s_FdoCommandType[FdoCommandType_CreateLongTransactionCheckpoint]    = "CreateLongTransactionCheckpoint";
    s_FdoCommandType[FdoCommandType_GetLongTransactionCheckpoints]      = "GetLongTransactionCheckpoints";
    s_FdoCommandType[FdoCommandType_RollbackLongTransactionCheckpoint]  = "RollbackLongTransactionCheckpoint";
    s_FdoCommandType[FdoCommandType_ChangeLongTransactionPrivileges]    = "ChangeLongTransactionPrivileges";
    s_FdoCommandType[FdoCommandType_GetLongTransactionPrivileges]       = "GetLongTransactionPrivileges";
    s_FdoCommandType[FdoCommandType_ChangeLongTransactionSet]           = "ChangeLongTransactionSet";
    s_FdoCommandType[FdoCommandType_GetLongTransactionsInSet]           = "GetLongTransactionsInSet";
    s_FdoCommandType[FdoCommandType_NetworkShortestPath]                = "NetworkShortestPath";
    s_FdoCommandType[FdoCommandType_NetworkAllPaths]                    = "NetworkAllPaths";
    s_FdoCommandType[FdoCommandType_NetworkReachableNodes]              = "NetworkReachableNodes";
    s_FdoCommandType[FdoCommandType_NetworkReachingNodes]               = "NetworkReachingNodes";
    s_FdoCommandType[FdoCommandType_NetworkNearestNeighbors]            = "NetworkNearestNeighbors";
    s_FdoCommandType[FdoCommandType_NetworkWithinCost]                  = "NetworkWithinCost";
    s_FdoCommandType[FdoCommandType_NetworkTSP]                         = "NetworkTSP";
    s_FdoCommandType[FdoCommandType_ActivateTopologyArea]               = "ActivateTopologyArea";
    s_FdoCommandType[FdoCommandType_DeactivateTopologyArea]             = "DeactivateTopologyArea";
    s_FdoCommandType[FdoCommandType_ActivateTopologyInCommandResult]    = "ActivateTopologyInCommandResult";
    s_FdoCommandType[FdoCommandType_DeactivateTopologyInCommandResults] = "DeactivateTopologyInCommandResults";
    s_FdoCommandType[FdoCommandType_SelectAggregates]                   = "SelectAggregates";
    s_FdoCommandType[FdoCommandType_CreateDataStore]                    = "CreateDataStore";
    s_FdoCommandType[FdoCommandType_DestroyDataStore]                   = "DestroyDataStore";
    s_FdoCommandType[FdoCommandType_ListDataStores]                     = "ListDataStores";
    s_FdoCommandType[FdoCommandType_FirstProviderCommand]               = "FirstProviderCommand";

    // Condition types
    s_FdoConditionType[FdoConditionType_Comparison]                     = "Comparison";
    s_FdoConditionType[FdoConditionType_Like]                           = "Like";
    s_FdoConditionType[FdoConditionType_In]                             = "In";
    s_FdoConditionType[FdoConditionType_Null]                           = "Null";
    s_FdoConditionType[FdoConditionType_Spatial]                        = "Spatial";
    s_FdoConditionType[FdoConditionType_Distance]                       = "Distance";

    // Spatial Operations
    s_FdoSpatialOperations[FdoSpatialOperations_Contains]               = "Contains";
    s_FdoSpatialOperations[FdoSpatialOperations_Crosses]                = "Crosses";
    s_FdoSpatialOperations[FdoSpatialOperations_Disjoint]               = "Disjoint";
    s_FdoSpatialOperations[FdoSpatialOperations_Equals]                 = "Equals";
    s_FdoSpatialOperations[FdoSpatialOperations_Intersects]             = "Intersects";
    s_FdoSpatialOperations[FdoSpatialOperations_Overlaps]               = "Overlaps";
    s_FdoSpatialOperations[FdoSpatialOperations_Touches]                = "Touches";
    s_FdoSpatialOperations[FdoSpatialOperations_Within]                 = "Within";
    s_FdoSpatialOperations[FdoSpatialOperations_CoveredBy]              = "CoveredBy";
    s_FdoSpatialOperations[FdoSpatialOperations_Inside]                 = "Inside";
    s_FdoSpatialOperations[FdoSpatialOperations_EnvelopeIntersects]     = "EnvelopeIntersects";

    // Distance Operation
    s_FdoDistanceOperations[FdoDistanceOperations_Beyond]               = "Beyond";
    s_FdoDistanceOperations[FdoDistanceOperations_Within]               = "Within";

    // Expression Type
    s_FdoExpressionType[FdoExpressionType_Basic]                        = "Basic";
    s_FdoExpressionType[FdoExpressionType_Function]                     = "Function";
    s_FdoExpressionType[FdoExpressionType_Parameter]                    = "Parameter";

    // Geometry Type
    s_FdoGeometryType[FdoGeometryType_None]                     = "None";
    s_FdoGeometryType[FdoGeometryType_Point]                    = "Point";
    s_FdoGeometryType[FdoGeometryType_LineString]               = "LineString";
    s_FdoGeometryType[FdoGeometryType_Polygon]                  = "Polygon";
    s_FdoGeometryType[FdoGeometryType_MultiPoint]               = "MultiPoint";
    s_FdoGeometryType[FdoGeometryType_MultiLineString]          = "MultiLineString";
    s_FdoGeometryType[FdoGeometryType_MultiPolygon]             = "MultiPolygon";
    s_FdoGeometryType[FdoGeometryType_MultiGeometry]            = "MultiGeometry";
    s_FdoGeometryType[FdoGeometryType_CurveString]              = "CurveString";
    s_FdoGeometryType[FdoGeometryType_CurvePolygon]             = "CurvePolygon";
    s_FdoGeometryType[FdoGeometryType_MultiCurveString]         = "MultiCurveString";
    s_FdoGeometryType[FdoGeometryType_MultiCurvePolygon]        = "MultiCurvePolygon";

    // Geometry component type
    s_FdoGeometryComponentType[FdoGeometryComponentType_LinearRing]         = "LinearRing";
    s_FdoGeometryComponentType[FdoGeometryComponentType_CircularArcSegment] = "ArcSegment";
    s_FdoGeometryComponentType[FdoGeometryComponentType_LineStringSegment]  = "LinearSegment";
    s_FdoGeometryComponentType[FdoGeometryComponentType_Ring]               = "CurveRing";

    return true;
}

MgByteReader* MgdFeatureService::GetFeatureProviders() 
{ 
    STRING xml = L"<FeatureProviderRegistry>\n";

    FdoPtr<IProviderRegistry> reg = FdoFeatureAccessManager::GetProviderRegistry();
    const FdoProviderCollection* providers = reg->GetProviders();
    FdoPtr<IConnectionManager> connMgr = FdoFeatureAccessManager::GetConnectionManager();

    for (INT32 i = 0; i < providers->GetCount(); i++)
    {
        FdoPtr<FdoProvider> provider;
        FdoString* name = NULL;
        FdoString* disp = NULL;
        FdoString* desc = NULL;
        FdoString* ver = NULL;
        FdoString* fdoVer = NULL;

        FdoPtr<FdoIConnection> conn;
        FdoPtr<FdoIConnectionInfo> connInfo;
        FdoPtr<FdoIConnectionPropertyDictionary> connDict;

        bool ok = false;
        try 
        {
            provider = providers->GetItem(i);
            name = provider->GetName();
            disp = provider->GetDisplayName();
            desc = provider->GetDescription();
            ver = provider->GetVersion();
            fdoVer = provider->GetFeatureDataObjectsVersion();

            conn = connMgr->CreateConnection(name);
            connInfo = conn->GetConnectionInfo();
            connDict = connInfo->GetConnectionProperties();

            ok = true;
        }
        catch(FdoException* ex) //Creating a FDO provider connection with missing third party dependencies
        {
            ex->Release();
            ok = false;
        }
        catch (...)
        {
            ok = false;
        }

        if (!ok)
            continue;

        xml += L"\t<FeatureProvider>\n";
        
        xml += L"\t\t<Name>";
        xml += name;
        xml += L"</Name>\n";

        xml += L"\t\t<DisplayName>";
        xml += disp;
        xml += L"</DisplayName>\n";

        xml += L"\t\t<Description>";
        xml += desc;
        xml += L"</Description>\n";

        xml += L"\t\t<Version>";
        xml += ver;
        xml += L"</Version>\n";

        xml += L"\t\t<FeatureDataObjectsVersion>";
        xml += fdoVer;
        xml += L"</FeatureDataObjectsVersion>\n";

        xml += L"\t\t<ConnectionProperties>\n";
        
        FdoInt32 pCount;
        FdoString** propNames = connDict->GetPropertyNames(pCount);
        for (INT32 j = 0; j < pCount; j++)
        {
            FdoString* pName = propNames[j];
            FdoString* plName = connDict->GetLocalizedName(pName);
            FdoString* pDefault = connDict->GetPropertyDefault(pName);

            bool pRequired = connDict->IsPropertyRequired(pName);
            bool pProtected = connDict->IsPropertyProtected(pName);
            bool pEnumerable = connDict->IsPropertyEnumerable(pName);

            xml += L"\t\t\t<ConnectionProperty ";
            xml += L"Required=\"";
            xml += pRequired ? L"true" : L"false";
            xml += L"\" Protected=\"";
            xml += pProtected ? L"true" : L"false";
            xml += L"\" Enumerable=\"";
            xml += pEnumerable ? L"true" : L"false";
            xml += L"\">\n";

            xml += L"\t\t<Name>";
            xml += pName;
            xml += L"</Name>\n";

            xml += L"\t\t<LocalizedName>";
            xml += disp;
            xml += L"</LocalizedName>\n";

            xml += L"\t\t<DefaultValue>";
            xml += desc;
            xml += L"</DefaultValue>\n";

            if (pEnumerable)
            {
                try 
                {
                    FdoInt32 pvCount;
                    FdoString** pValues = connDict->EnumeratePropertyValues(pName, pvCount);

                    for (INT32 k = 0; k < pvCount; k++)
                    {
                        FdoString* pVal = pValues[k];

                        xml += L"\t\t<Value>";
                        xml += pVal;
                        xml += L"</Value>\n";
                    }
                }
                catch (FdoException* ex)
                {
                    ex->Release();
                }
            }
            xml += L"\t\t\t</ConnectionProperty>\n";
        }

        xml += L"\t\t</ConnectionProperties>\n";

        xml += L"\t</FeatureProvider>\n";
    }

    xml += L"</FeatureProviderRegistry>\n";

    std::string cxml = MgUtil::WideCharToMultiByte(xml);

	Ptr<MgByteReader> reader;
    Ptr<MgByteSource> source = new MgByteSource((unsigned char*)cxml.c_str(), (INT32)cxml.length());
    source->SetMimeType(MgMimeType::Xml);
    reader = source->GetReader();

	return reader.Detach();
}

MgStringCollection* MgdFeatureService::GetConnectionPropertyValues(CREFSTRING providerName,
                                                        CREFSTRING propertyName,
                                                        CREFSTRING partialConnString) 
{ 
    if (providerName.empty())
        throw new MgInvalidArgumentException(L"MgdFeatureService::GetConnectionPropertyValues", __LINE__, __WFILE__, NULL, L"", NULL);

    if (propertyName.empty())
        throw new MgInvalidArgumentException(L"MgdFeatureService::GetConnectionPropertyValues", __LINE__, __WFILE__, NULL, L"", NULL);

    Ptr<MgStringCollection> values;

    MG_FEATURE_SERVICE_TRY()

    FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(providerName, partialConnString);
    //Only open the connection if non-empty connection string
    if (!partialConnString.empty())
        conn->Open();

    FdoPtr<FdoIConnectionInfo> connInfo = conn->GetConnectionInfo();
    FdoPtr<FdoIConnectionPropertyDictionary> connDict = connInfo->GetConnectionProperties();

    FdoInt32 pCount;
    FdoString** pValues = connDict->EnumeratePropertyValues(propertyName.c_str(), pCount);

    values = new MgStringCollection();

    for (INT32 i = 0; i < pCount; i++)
    {
        STRING val = pValues[i];
        values->Add(val);
    }

    MG_FEATURE_SERVICE_CATCH_AND_THROW(L"MgdFeatureService::GetConnectionPropertyValues")

	return values.Detach();
}

bool MgdFeatureService::TestConnection(CREFSTRING providerName, CREFSTRING connectionString) 
{
	bool ok = false;
	try
	{
		FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(providerName, connectionString);
		ok = (conn->Open() == FdoConnectionState_Open);
		conn->Close();
	}
	catch(FdoException* ex)
	{
		ex->Release();
		ok = false;
	}
	return ok;
}

bool MgdFeatureService::TestConnection(MgResourceIdentifier* resource) 
{ 
    CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::TestConnection");

	bool ok = false;

	try
	{
		FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(resource);
		ok = (conn->Open() == FdoConnectionState_Open);
		conn->Close();
	}
	catch (MgFdoException* ex)
	{
		ex->Release();
		ok = false;
	}

	return ok;
}

void MgdFeatureService::WriteCommandCapabilities(MgXmlUtil* caps, FdoICommandCapabilities* fcc)
{
    CHECKNULL(caps, L"MgdFeatureService::WriteCommandCapabilities");

    CHECKNULL(fcc, L"MgdFeatureService::WriteCommandCapabilities");

    DOMElement* root = caps->GetRootNode();
    CHECKNULL(root, L"MgdFeatureService::WriteCommandCapabilities");

    DOMElement* cmdNode = caps->AddChildNode(root, "Command");
    CHECKNULL(cmdNode, L"MgdFeatureService::WriteCommandCapabilities");

    // Add all command types
    FdoInt32 cnt = 0;
    FdoInt32* fcmd = fcc->GetCommands(cnt);
    if (cnt > 0 && fcmd != NULL)
    {
        DOMElement* scNode = caps->AddChildNode(cmdNode, "SupportedCommands");
        CHECKNULL(scNode, L"MgdFeatureService::WriteCommandCapabilities");

        for (FdoInt32 i=0; i < cnt; i++)
        {
            string cmdStr = s_FdoCommandType[(FdoCommandType)fcmd[i]];
            if (!cmdStr.empty())
            {
                switch (fcmd[i])
                {
                    case FdoCommandType_DescribeSchemaMapping:
                    case FdoCommandType_NetworkShortestPath:
                    case FdoCommandType_NetworkAllPaths:
                    case FdoCommandType_NetworkReachableNodes:
                    case FdoCommandType_NetworkReachingNodes:
                    case FdoCommandType_NetworkNearestNeighbors:
                    case FdoCommandType_NetworkWithinCost:
                    case FdoCommandType_NetworkTSP:
                    case FdoCommandType_ActivateTopologyArea:
                    case FdoCommandType_DeactivateTopologyArea:
                    case FdoCommandType_ActivateTopologyInCommandResult:
                    case FdoCommandType_DeactivateTopologyInCommandResults:
                    case FdoCommandType_SelectAggregates:
                    case FdoCommandType_CreateDataStore:
                    case FdoCommandType_DestroyDataStore:
                    case FdoCommandType_ListDataStores:
                        break;
                        // these enumerates were added for version 2
                        // fall thru to write this command
                    default:
                        caps->AddTextNode(scNode, "Name", cmdStr.c_str());
                        break;
                }
            }
        }
    }

    // Supports Parameters
    bool supportsParameters = fcc->SupportsParameters();
    caps->AddTextNode(cmdNode, "SupportsParameters", supportsParameters);

    // Supports Timeout
    bool supportsTimeout = fcc->SupportsTimeout();
    caps->AddTextNode(cmdNode, "SupportsTimeout", supportsTimeout);

    // Supports SupportsSelectExpressions
    bool supportsSelectExpressions = fcc->SupportsSelectExpressions();
    caps->AddTextNode(cmdNode, "SupportsSelectExpressions", supportsSelectExpressions);

    // Supports SupportsSelectFunctions
    bool supportsSelectFunctions = fcc->SupportsSelectFunctions();
    caps->AddTextNode(cmdNode, "SupportsSelectFunctions", supportsSelectFunctions);

    // Supports SupportsSelectDistinct
    bool supportsSelectDistinct = fcc->SupportsSelectDistinct();
    caps->AddTextNode(cmdNode, "SupportsSelectDistinct", supportsSelectDistinct);

    // Supports SupportsSelectOrdering
    bool supportsSelectOrdering = fcc->SupportsSelectOrdering();
    caps->AddTextNode(cmdNode, "SupportsSelectOrdering", supportsSelectOrdering);

    // Supports SupportsSelectGrouping
    bool supportsSelectGrouping = fcc->SupportsSelectGrouping();
    caps->AddTextNode(cmdNode, "SupportsSelectGrouping", supportsSelectGrouping);
}

void MgdFeatureService::WriteConnectionCapabilities(MgXmlUtil* caps, FdoIConnectionCapabilities* ficc)
{
    CHECKNULL(caps, L"MgdFeatureService::WriteConnectionCapabilities");

    DOMElement* root = caps->GetRootNode();
    CHECKNULL(root, L"MgdFeatureService::WriteConnectionCapabilities");

    DOMElement* connNode = caps->AddChildNode(root, "Connection");
    CHECKNULL(connNode, L"MgdFeatureService::WriteConnectionCapabilities");

    CHECKNULL((FdoIConnectionCapabilities*)ficc, L"MgdFeatureService::WriteConnectionCapabilities");

    // Thread
    FdoThreadCapability ftc = ficc->GetThreadCapability();
    string str = s_FdoThreadCapability[ftc];
    caps->AddTextNode(connNode, "ThreadCapability", str.c_str());

    // Spatial Context
    FdoInt32 cnt;
    FdoSpatialContextExtentType* fscet = ficc->GetSpatialContextTypes(cnt);
    if (cnt > 0 && fscet != NULL)
    {
        DOMElement* scNode = caps->AddChildNode(connNode, "SpatialContextExtent");
        CHECKNULL(scNode, L"MgdFeatureService::WriteConnectionCapabilities");

        for (FdoInt32 i = 0; i < cnt; i++)
        {
            string scStr = s_FdoSpatialContextExtentType[fscet[i]];
            caps->AddTextNode(scNode, "Type", scStr.c_str());
        }
    }

    // Locking
    bool supportsLocking = ficc->SupportsLocking();
    caps->AddTextNode(connNode, "SupportsLocking", supportsLocking);

    bool supportsTimeout = ficc->SupportsTimeout();
    caps->AddTextNode(connNode, "SupportsTimeout", supportsTimeout);

    bool supportsTransactions = ficc->SupportsTransactions();
    caps->AddTextNode(connNode, "SupportsTransactions", supportsTransactions);

    bool supportsLongTransactions = ficc->SupportsLongTransactions();
    caps->AddTextNode(connNode, "SupportsLongTransactions", supportsLongTransactions);

    bool supportsSQL = ficc->SupportsSQL();
    caps->AddTextNode(connNode, "SupportsSQL", supportsSQL);

    bool supportsConfiguration = ficc->SupportsConfiguration();
    caps->AddTextNode(connNode, "SupportsConfiguration", supportsConfiguration);
}

void MgdFeatureService::WriteFilterCapabilities(MgXmlUtil* caps, FdoIFilterCapabilities* ffc)
{
    CHECKNULL(caps, L"MgdFeatureService::WriteFilterCapabilities");

    CHECKNULL((FdoIFilterCapabilities*)ffc, L"MgdFeatureService::WriteFilterCapabilities");

    DOMElement* root = caps->GetRootNode();
    CHECKNULL(root, L"MgdFeatureService::WriteFilterCapabilities");

    DOMElement* filterNode = caps->AddChildNode(root, "Filter");
    CHECKNULL(filterNode, L"MgdFeatureService::WriteFilterCapabilities");

    // Add all condition types
    FdoInt32 cnt = 0;
    FdoConditionType* fct = ffc->GetConditionTypes(cnt);
    if (cnt > 0 && fct != NULL)
    {
        DOMElement* condNode = caps->AddChildNode(filterNode, "Condition");
        CHECKNULL(condNode, L"MgdFeatureService::WriteFilterCapabilities");

        for (FdoInt32 i=0; i < cnt; i++)
        {
            string condStr = s_FdoConditionType[fct[i]];
            caps->AddTextNode(condNode, "Type", condStr.c_str());
        }
    }

    // All spatial operations
    cnt = 0;
    FdoSpatialOperations* fso = ffc->GetSpatialOperations(cnt);
    if (cnt > 0 && fso != NULL)
    {
        DOMElement* fsoNode = caps->AddChildNode(filterNode, "Spatial");
        CHECKNULL(fsoNode, L"MgdFeatureService::WriteFilterCapabilities");

        for (FdoInt32 i=0; i < cnt; i++)
        {
            string operStr = s_FdoSpatialOperations[fso[i]];
            caps->AddTextNode(fsoNode, "Operation", operStr.c_str());
        }
    }

    // All distance operations
    cnt = 0;
    FdoDistanceOperations* fdo = ffc->GetDistanceOperations(cnt);
    if (cnt > 0 && fdo != NULL)
    {
        DOMElement* distNode = caps->AddChildNode(filterNode, "Distance");
        CHECKNULL(distNode, L"MgdFeatureService::WriteFilterCapabilities");

        for (FdoInt32 i=0; i < cnt; i++)
        {
            string fdoStr = s_FdoDistanceOperations[fdo[i]];
            caps->AddTextNode(distNode, "Operation", fdoStr.c_str());
        }
    }

    // supports Geodesic Distance
    bool supportsGeodesicDistance = ffc->SupportsGeodesicDistance();
    caps->AddTextNode(filterNode, "SupportsGeodesicDistance", supportsGeodesicDistance);

    // supports NonLiteral Geometric Operations
    bool supportsNonLiteralGeometricOperations = ffc->SupportsNonLiteralGeometricOperations();
    caps->AddTextNode(filterNode, "SupportsNonLiteralGeometricOperations", supportsNonLiteralGeometricOperations);
}

void MgdFeatureService::WriteGeometryCapabilities(MgXmlUtil* caps, FdoIGeometryCapabilities* fgc)
{
    CHECKNULL(caps, L"MgdFeatureService::WriteGeometryCapabilities");

    MG_FEATURE_SERVICE_TRY()

    // Provider has no geometric capabilities
    if (NULL == (FdoIGeometryCapabilities*)fgc)
    {
        return;
    }

    DOMElement* root = caps->GetRootNode();
    CHECKNULL(root, L"MgdFeatureService::WriteGeometryCapabilities");

    DOMElement* geometryNode = caps->AddChildNode(root, "Geometry");
    CHECKNULL(geometryNode, L"MgdFeatureService::WriteGeometryCapabilities");

    FdoInt32 cnt = 0;
    FdoGeometryType* geomType = fgc->GetGeometryTypes(cnt);
    if (cnt > 0 && geomType != NULL)
    {
        DOMElement* geometryTypeNode = caps->AddChildNode(geometryNode, "Types");
        CHECKNULL(geometryTypeNode, L"MgdFeatureService::WriteGeometryCapabilities");

        for (FdoInt32 i=0; i < cnt; i++)
        {
            string geomTypeStr = s_FdoGeometryType[geomType[i]];
            caps->AddTextNode(geometryTypeNode, "Type", geomTypeStr.c_str());
        }
    }

    FdoGeometryComponentType* geomCompType = fgc->GetGeometryComponentTypes(cnt);
    if (cnt > 0 && geomCompType != NULL)
    {
        DOMElement* geometryCompNode = caps->AddChildNode(geometryNode, "Components");
        CHECKNULL(geometryCompNode, L"MgdFeatureService::WriteGeometryCapabilities");

        for (FdoInt32 i=0; i < cnt; i++)
        {
            string geomCompStr = s_FdoGeometryComponentType[geomCompType[i]];
            caps->AddTextNode(geometryCompNode, "Type", geomCompStr.c_str());
        }
    }

    FdoInt32 dim = fgc->GetDimensionalities();

    char buff[8]; buff[0] = 0;
    sprintf(buff, "%d", dim);

    caps->AddTextNode(geometryNode, "Dimensionality", &buff[0]);

    MG_FEATURE_SERVICE_CATCH_AND_THROW(L"MgdFeatureService::WriteGeometryCapabilities")
}

void MgdFeatureService::WriteExpressionCapabilities(MgXmlUtil* caps, FdoIExpressionCapabilities* fec)
{
    CHECKNULL(caps, L"MgdFeatureService::WriteExpressionCapabilities");

    CHECKNULL((FdoIExpressionCapabilities*)fec, L"MgdFeatureService::WriteExpressionCapabilities");

    DOMElement* root = caps->GetRootNode();
    CHECKNULL(root, L"MgdFeatureService::WriteExpressionCapabilities");

    DOMElement* expressionNode = caps->AddChildNode(root, "Expression");
    CHECKNULL(expressionNode, L"MgdFeatureService::WriteExpressionCapabilities");

    // Add all expression types
    FdoInt32 cnt = 0;
    FdoExpressionType* fet = fec->GetExpressionTypes(cnt);
    if (cnt > 0 && fet != NULL)
    {
        DOMElement* typeNode = caps->AddChildNode(expressionNode, "Type");
        CHECKNULL(typeNode, L"MgdFeatureService::WriteExpressionCapabilities");

        for (FdoInt32 i=0; i < cnt; i++)
        {
            string typeStr = s_FdoExpressionType[fet[i]];
            caps->AddTextNode(typeNode, "Name", typeStr.c_str());
        }
    }

    // Add all functions available
    cnt = 0;
    FdoPtr<FdoFunctionDefinitionCollection> ffdc = fec->GetFunctions();
    if (NULL != (FdoFunctionDefinitionCollection*)ffdc)
    {
        FdoInt32 funcCnt = ffdc->GetCount();
        if (funcCnt > 0)
        {
            // Add function definition collection element if there are any functions available
            DOMElement* funcDefColNode = caps->AddChildNode(expressionNode, "FunctionDefinitionList");
            CHECKNULL(funcDefColNode, L"MgdFeatureService::WriteExpressionCapabilities");

            for (FdoInt32 i=0; i < funcCnt; i++)
            {
                // Add function definition element
                FdoPtr<FdoFunctionDefinition> ffd = ffdc->GetItem(i);
                CHECKNULL((FdoFunctionDefinition*)ffd, L"MgdFeatureService::WriteExpressionCapabilities");

                DOMElement* funcDefNode = caps->AddChildNode(funcDefColNode, "FunctionDefinition");
                CHECKNULL(funcDefNode, L"MgdFeatureService::WriteExpressionCapabilities");

                const char* strName = MgUtil::WideCharToMultiByte(ffd->GetName());
                const char* strDesc = MgUtil::WideCharToMultiByte(ffd->GetDescription());

                FdoFunctionCategoryType eFdoFunctionCategoryType = ffd->GetFunctionCategoryType();
                string strFunctionCategoryType = s_FdoFunctionCategoryType[eFdoFunctionCategoryType];

                caps->AddTextNode(funcDefNode, "Name", strName);
                caps->AddTextNode(funcDefNode, "Description", strDesc);
                caps->AddTextNode(funcDefNode, "CategoryType",  strFunctionCategoryType.c_str());
                caps->AddTextNode(funcDefNode, "IsAggregate",  ffd->IsAggregate());
                caps->AddTextNode(funcDefNode, "IsSupportsVariableArgumentsList",  ffd->SupportsVariableArgumentsList());

                delete[] strName;
                delete[] strDesc;

                // Add argument of each functions if there are any
                FdoPtr<FdoReadOnlySignatureDefinitionCollection> signatures = ffd->GetSignatures();
                if (NULL != (FdoReadOnlySignatureDefinitionCollection*)signatures)
                {
                    FdoInt32 signaturesCnt = signatures->GetCount();
                    if (signaturesCnt > 0)
                    {

                        DOMElement* signDefColNode = caps->AddChildNode(funcDefNode, "SignatureDefinitionCollection");
                        CHECKNULL(signDefColNode, L"MgdFeatureService::WriteExpressionCapabilities");
                        for (FdoInt32 j=0; j < signaturesCnt; j++)
                        {

                            // Add SignatureDefinition for each signature
                            FdoPtr<FdoSignatureDefinition> fsd = signatures->GetItem(j);
                            CHECKNULL((FdoSignatureDefinition*)fsd, L"MgdFeatureService::WriteExpressionCapabilities");

                            DOMElement* signDefNode = caps->AddChildNode(signDefColNode, "SignatureDefinition");
                            CHECKNULL(signDefNode, L"MgdFeatureService::WriteExpressionCapabilities");

                            FdoPropertyType eSignPropertyDataType = fsd->GetReturnPropertyType();
                            string strSignPropertyType = s_FdoPropertyTypeAsString[eSignPropertyDataType];
                            string strSignDataType;
                            if (eSignPropertyDataType == FdoPropertyType_DataProperty)
                            {
                                FdoDataType eSignDataType = fsd->GetReturnType();
                                strSignDataType = s_FdoDataType[eSignDataType];
                            }

                            caps->AddTextNode(signDefNode, "PropertyType", strSignPropertyType.c_str());
                            if (eSignPropertyDataType == FdoPropertyType_DataProperty)
                                caps->AddTextNode(signDefNode, "DataType",  strSignDataType.c_str());

                            DOMElement* argDefColNode = caps->AddChildNode(signDefNode, "ArgumentDefinitionList");
                            CHECKNULL(argDefColNode, L"MgdFeatureService::WriteExpressionCapabilities");

                            FdoPtr<FdoReadOnlyArgumentDefinitionCollection> fads = fsd->GetArguments();
                            if (NULL != (FdoReadOnlyArgumentDefinitionCollection *) fads)
                            {
                                FdoInt32 argCnt = fads->GetCount();
                                if (argCnt > 0)
                                {
                                    for (int k=0; k<argCnt; k++)
                                    {
                                        FdoPtr<FdoArgumentDefinition> fad = fads->GetItem(k);
                                        CHECKNULL((FdoArgumentDefinition*)fad, L"MgdFeatureService::WriteExpressionCapabilities");

                                        DOMElement* argDefNode = caps->AddChildNode(argDefColNode, "ArgumentDefinition");
                                        CHECKNULL(argDefNode, L"MgdFeatureService::WriteExpressionCapabilities");

                                        const char* strArgName = MgUtil::WideCharToMultiByte(fad->GetName());
                                        const char* strArgDesc = MgUtil::WideCharToMultiByte(fad->GetDescription());

                                        FdoPropertyType eArgPropertyDataType = fad->GetPropertyType();
                                        string strArgPropertyType = s_FdoPropertyTypeAsString[eArgPropertyDataType];
                                        string strArgDataType;
                                        if (eArgPropertyDataType == FdoPropertyType_DataProperty)
                                        {
                                            FdoDataType eArgDataType = fad->GetDataType();
                                            strArgDataType = s_FdoDataType[eArgDataType];
                                        }

                                        caps->AddTextNode(argDefNode, "Name", strArgName);
                                        caps->AddTextNode(argDefNode, "Description", strArgDesc);
                                        caps->AddTextNode(argDefNode, "PropertyType", strArgPropertyType.c_str());
                                        if (eArgPropertyDataType == FdoPropertyType_DataProperty)
                                            caps->AddTextNode(argDefNode, "DataType",  strArgDataType.c_str());

                                        delete[] strArgName;
                                        delete[] strArgDesc;

                                        FdoPtr<FdoPropertyValueConstraintList> fpvc = fad->GetArgumentValueList();
                                        if (NULL != (FdoPropertyValueConstraintList *) fpvc)
                                        {
                                            if (fpvc->GetConstraintType() == FdoPropertyValueConstraintType_List)
                                            {
                                                DOMElement* propValueConstListNode = caps->AddChildNode(argDefNode, "PropertyValueConstraintList");
                                                CHECKNULL(propValueConstListNode, L"MgdFeatureService::WriteExpressionCapabilities");

                                                FdoPropertyValueConstraintType eConstraintType = fpvc->GetConstraintType();

                                                FdoPtr<FdoDataValueCollection> dvc = fpvc->GetConstraintList();
                                                if (NULL != (FdoDataValueCollection *) dvc)
                                                {
                                                    FdoInt32 dvCnt = dvc->GetCount();
                                                    if (dvCnt > 0)
                                                    {
                                                        for (int l=0; l<dvCnt; l++)
                                                        {
                                                            FdoPtr<FdoDataValue> dv = dvc->GetItem(l);
                                                            CHECKNULL((FdoDataValue*)dv, L"MgdFeatureService::WriteExpressionCapabilities");
                                                            FdoDataType dataType = dv->GetDataType();
                                                            // FdoDataType_String is the only supported type
                                                            if (dataType == FdoDataType_String)
                                                            {
                                                                FdoStringValue *stringValue = (FdoStringValue *) dv.p;
                                                                FdoString* xmlValue = stringValue->GetString();
                                                                const char* strDataValue = MgUtil::WideCharToMultiByte(stringValue->GetString());
                                                                caps->AddTextNode(propValueConstListNode, "Value", xmlValue);
                                                                delete[] strDataValue;
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

void MgdFeatureService::WriteSchemaCapabilities(MgXmlUtil* caps, FdoISchemaCapabilities* fsc)
{
    CHECKNULL(caps, L"MgdFeatureService::WriteSchemaCapabilities");
    CHECKNULL((FdoISchemaCapabilities*)fsc, L"MgdFeatureService::WriteSchemaCapabilities");

    DOMElement* root = caps->GetRootNode();
    CHECKNULL(root, L"MgdFeatureService::WriteSchemaCapabilities");

    DOMElement* schemaNode = caps->AddChildNode(root, "Schema");
    CHECKNULL(schemaNode, L"MgdFeatureService::WriteSchemaCapabilities");

    // Add all class types
    FdoInt32 cnt = 0;
    FdoClassType* fct = fsc->GetClassTypes(cnt);
    if (cnt > 0 && fct != NULL)
    {
        DOMElement* classNode = caps->AddChildNode(schemaNode, "Class");
        CHECKNULL(classNode, L"MgdFeatureService::WriteSchemaCapabilities");

        for (FdoInt32 i=0; i < cnt; i++)
        {
            string scStr = s_FdoClassType[fct[i]];
            caps->AddTextNode(classNode, "Type", scStr.c_str());
        }
    }

    // Add all data types
    cnt = 0;
    FdoDataType* fdt = fsc->GetDataTypes(cnt);
    if (cnt > 0 && fdt != NULL)
    {
        DOMElement* dataNode = caps->AddChildNode(schemaNode, "Data");
        CHECKNULL(dataNode, L"MgdFeatureService::WriteSchemaCapabilities");

        for (FdoInt32 i=0; i < cnt; i++)
        {
            string dtStr = s_FdoDataType[fdt[i]];
            caps->AddTextNode(dataNode, "Type", dtStr.c_str());
        }
    }

    // Supports Inheritance
    bool supportsInheritance        = fsc->SupportsInheritance();
    caps->AddTextNode(schemaNode, "SupportsInheritance", supportsInheritance);

    // Supports Multiple Schemas
    bool supportsMultipleSchemas    = fsc->SupportsMultipleSchemas();
    caps->AddTextNode(schemaNode, "SupportsMultipleSchemas", supportsMultipleSchemas);

    bool supportsObjectProperties   = fsc->SupportsObjectProperties();
    caps->AddTextNode(schemaNode, "SupportsObjectProperties", supportsObjectProperties);

    bool supportsAssociationProperties = fsc->SupportsAssociationProperties();
    caps->AddTextNode(schemaNode, "SupportsAssociationProperties", supportsAssociationProperties);

    bool supportsSchemaOverrides =  fsc->SupportsSchemaOverrides();
    caps->AddTextNode(schemaNode, "SupportsSchemaOverrides", supportsSchemaOverrides);

    bool supportsNetworkModel  = fsc->SupportsNetworkModel();
    caps->AddTextNode(schemaNode, "SupportsNetworkModel", supportsNetworkModel);

    bool supportsAutoIdGeneration  = fsc->SupportsAutoIdGeneration();
    caps->AddTextNode(schemaNode, "SupportsAutoIdGeneration", supportsAutoIdGeneration);

    bool supportsDataStoreScopeUniqueIdGeneration  = fsc->SupportsDataStoreScopeUniqueIdGeneration();
    caps->AddTextNode(schemaNode, "SupportsDataStoreScopeUniqueIdGeneration", supportsDataStoreScopeUniqueIdGeneration);

    FdoDataType* sagt = fsc->GetSupportedAutoGeneratedTypes(cnt);
    if (cnt > 0 && sagt != NULL)
    {
        DOMElement* sagtNode = caps->AddChildNode(schemaNode, "SupportedAutoGeneratedTypes");
        CHECKNULL(sagtNode, L"MgdFeatureService::WriteSchemaCapabilities");

        for (FdoInt32 i=0; i < cnt; i++)
        {
            string sagtStr = s_FdoDataType[sagt[i]];
            caps->AddTextNode(sagtNode, "Type", sagtStr.c_str());
        }
    }

    bool supportsSchemaModification  = fsc->SupportsSchemaModification();
    caps->AddTextNode(schemaNode, "SupportsSchemaModification", supportsSchemaModification);
}

void MgdFeatureService::WriteRasterCapabilities(MgXmlUtil* caps, FdoIRasterCapabilities* frc)
{
    CHECKNULL(caps, L"MgdFeatureService::WriteRasterCapabilities");
    CHECKNULL((FdoIRasterCapabilities*)frc, L"MgdFeatureService::WriteRasterCapabilities");

    DOMElement* root = caps->GetRootNode();
    CHECKNULL(root, L"MgdFeatureService::WriteRasterCapabilities");

    DOMElement* rasterNode = caps->AddChildNode(root, "Raster");
    CHECKNULL(rasterNode, L"MgdFeatureService::WriteRasterCapabilities");

    // Supports Raster
    bool supportsRaster = frc->SupportsRaster();
    caps->AddTextNode(rasterNode, "SupportsRaster", supportsRaster);

    // Supports Stitching
    bool supportsStitching = frc->SupportsStitching();
    caps->AddTextNode(rasterNode, "SupportsStitching", supportsStitching);

    bool supportsSubsampling = frc->SupportsSubsampling();
    caps->AddTextNode(rasterNode, "SupportsSubsampling", supportsSubsampling);
}

void MgdFeatureService::WriteTopologyCapabilities(MgXmlUtil* caps, FdoITopologyCapabilities* frc)
{
    CHECKNULL(caps, L"MgdFeatureService::WriteTopologyCapabilities");

    MG_FEATURE_SERVICE_TRY()
    // Provider has no topology capabilities
    if (NULL == (FdoITopologyCapabilities*)frc)
    {
        return;
    }

    DOMElement* root = caps->GetRootNode();
    CHECKNULL(root, L"MgdFeatureService::WriteTopologyCapabilities");

    DOMElement* topologyNode = caps->AddChildNode(root, "Topology");
    CHECKNULL(topologyNode, L"MgdFeatureService::WriteTopologyCapabilities");

    // Supports Topology
    bool supportsTopology = frc->SupportsTopology();
    caps->AddTextNode(topologyNode, "SupportsTopology", supportsTopology);

    // Supports Stitching
    bool supportsTopologicalHierarchy = frc->SupportsTopologicalHierarchy();
    caps->AddTextNode(topologyNode, "SupportsTopologicalHierarchy", supportsTopologicalHierarchy);

    bool breaksCurveCrossingsAutomatically = frc->BreaksCurveCrossingsAutomatically();
    caps->AddTextNode(topologyNode, "BreaksCurveCrossingsAutomatically", breaksCurveCrossingsAutomatically);

    bool activatesTopologyByArea = frc->ActivatesTopologyByArea();
    caps->AddTextNode(topologyNode, "ActivatesTopologyByArea", activatesTopologyByArea);

    bool constrainsFeatureMovements = frc->ConstrainsFeatureMovements();
    caps->AddTextNode(topologyNode, "ConstrainsFeatureMovements", constrainsFeatureMovements);

    // TODO: Change this to CATCH_AND_THROW when SimpleDB stops throwing exception of not implemented
    MG_FEATURE_SERVICE_CATCH(L"MgServerGetProviderCapabilities.CreateTopologyCapabilities")

}

MgByteReader* MgdFeatureService::GetCapabilities(CREFSTRING providerName) 
{
	return GetCapabilities(providerName, L"");
}

MgByteReader* MgdFeatureService::GetCapabilities(CREFSTRING providerName, CREFSTRING connectionString) 
{ 
    if (providerName.empty())
        throw new MgInvalidArgumentException(L"MgdFeatureService::GetCapabilities", __LINE__, __WFILE__, NULL, L"", NULL);

    Ptr<MgByteReader> caps;

    MG_FEATURE_SERVICE_TRY()

    FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(providerName, connectionString);

    // Root node element created
    // this XML follows the FdoProviderCapabilities-1.0.0.xsd schema
    std::auto_ptr<MgXmlUtil> xml(new MgXmlUtil("FeatureProviderCapabilities"));
    CHECKNULL(xml.get(), L"MgdFeatureService::GetCapabilities");
    DOMElement* root = xml->GetRootNode();

    // Provide name element and its attribute
    DOMElement* providerNode = xml->AddChildNode(root, "Provider");
    xml->SetAttribute(providerNode, "Name", providerName.c_str());

    FdoPtr<FdoICommandCapabilities> cmdCaps = conn->GetCommandCapabilities();
    FdoPtr<FdoIConnectionCapabilities> connCaps = conn->GetConnectionCapabilities();
    FdoPtr<FdoIExpressionCapabilities> exprCaps = conn->GetExpressionCapabilities();
    FdoPtr<FdoIFilterCapabilities> filterCaps = conn->GetFilterCapabilities();
    FdoPtr<FdoIGeometryCapabilities> geomCaps = conn->GetGeometryCapabilities();
    FdoPtr<FdoIRasterCapabilities> rasterCaps = conn->GetRasterCapabilities();
    FdoPtr<FdoISchemaCapabilities> schemaCaps = conn->GetSchemaCapabilities();
    FdoPtr<FdoITopologyCapabilities> topoCaps = conn->GetTopologyCapabilities();

    WriteConnectionCapabilities(xml.get(), connCaps);
    WriteSchemaCapabilities(xml.get(), schemaCaps);
    WriteCommandCapabilities(xml.get(), cmdCaps);
    WriteFilterCapabilities(xml.get(), filterCaps);
    WriteExpressionCapabilities(xml.get(), exprCaps);
    WriteRasterCapabilities(xml.get(), rasterCaps);
    WriteTopologyCapabilities(xml.get(), topoCaps);
    WriteGeometryCapabilities(xml.get(), geomCaps);

    caps = xml->ToReader();

    MG_FEATURE_SERVICE_CATCH_AND_THROW(L"MgdFeatureService::GetCapabilities")

	return caps.Detach();
}

void MgdFeatureService::ApplySchema(MgResourceIdentifier* resource, MgFeatureSchema* schema) 
{ 
    CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::ApplySchema");
    CHECKARGUMENTNULL(schema, L"MgdFeatureService::ApplySchema");

	MG_FEATURE_SERVICE_TRY()

    // Connect to provider
    FdoPtr<FdoIConnection> fdoConn = MgFdoConnectionUtil::CreateConnection(resource);
    fdoConn->Open();
    
    FdoPtr<FdoICommandCapabilities> cmdCaps = fdoConn->GetCommandCapabilities();

    // connection must be open
    bool bRefresh = false;
    bool bApplySchema = false;
    bool bDescribeSchema = false;

    FdoInt32 cmdCount;
    FdoInt32* cmds = cmdCaps->GetCommands(cmdCount);

    for (INT32 i = 0; i < cmdCount; i++)
    {
        if (cmds[i] == FdoCommandType_ApplySchema)
            bApplySchema = true;
        else if (cmds[i] == FdoCommandType_DescribeSchema)
            bDescribeSchema = true;
    }

    // Check whether command is supported by provider
    if (!bApplySchema || !bDescribeSchema)
    {
        // TODO: specify which argument and message, once we have the mechanism
        STRING message = MgFeatureUtil::GetMessage(L"MgCommandNotSupported");
        throw new MgInvalidOperationException(L"MgdFeatureService::ApplySchema", __LINE__, __WFILE__, NULL, L"", NULL);
    }

    FdoPtr<FdoIDescribeSchema> fdoDecribeSchemaCmd = (FdoIDescribeSchema*) fdoConn->CreateCommand(FdoCommandType_DescribeSchema);
    CHECKNULL((FdoIDescribeSchema*)fdoDecribeSchemaCmd, L"MgdFeatureService::ApplySchema");

    FdoPtr<FdoFeatureSchemaCollection> schemas = fdoDecribeSchemaCmd->Execute();
    CHECKNULL((FdoFeatureSchemaCollection*)schemas, L"MgdFeatureService::ApplySchema");

    FdoPtr<FdoIApplySchema> fdoApplySchemaCmd = (FdoIApplySchema*)fdoConn->CreateCommand(FdoCommandType_ApplySchema);
    CHECKNULL((FdoIApplySchema*)fdoApplySchemaCmd, L"MgdFeatureService::ApplySchema");

    STRING schemaName = schema->GetName();
    FdoPtr<FdoFeatureSchema> fdoOldSchema = schemas->FindItem(schemaName.c_str());
    if (NULL == fdoOldSchema)
    {
        if (!schema->IsDeleted())
        {
            FdoPtr<FdoFeatureSchema> fdoNewSchema = MgFeatureUtil::GetFdoFeatureSchema(schema);
            fdoApplySchemaCmd->SetFeatureSchema(fdoNewSchema);
            fdoApplySchemaCmd->Execute();
            bRefresh = true;
        }
    }
    else
    {
        if (!schema->IsDeleted())
            MgFeatureUtil::UpdateFdoFeatureSchema(schema, fdoOldSchema);
        else
            fdoOldSchema->Delete();

        fdoApplySchemaCmd->SetFeatureSchema(fdoOldSchema);
        fdoApplySchemaCmd->Execute();
        bRefresh = true;
    }

    // Clear the cached schema so that MgFeatureService::DescribeSchema
    // can return the correct schema
    if (bRefresh)
    {
        MgFeatureServiceCache* cache = MgFeatureServiceCache::GetInstance();
        cache->RemoveEntry(resource);
    }

    MG_FEATURE_SERVICE_CHECK_CONNECTION_CATCH_AND_THROW(resource, L"MgdFeatureService::ApplySchema")
}

MgFeatureSchemaCollection* MgdFeatureService::DescribeSchema(MgResourceIdentifier* resource,
                                                  CREFSTRING schemaName,
                                                  MgStringCollection* classNames) 
{
    return DescribeSchema(resource, schemaName, classNames, true);
}

MgFeatureSchemaCollection* MgdFeatureService::DescribeSchema(MgResourceIdentifier* resource,
                                                  CREFSTRING schemaName,
                                                  MgStringCollection* classNames,
                                                  bool serialize) 
{ 
	CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::DescribeSchema");

	FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(resource);
	conn->Open();

	FdoPtr<FdoIDescribeSchema> describe = (FdoIDescribeSchema*)conn->CreateCommand(FdoCommandType_DescribeSchema);
    if (!schemaName.empty())
	    describe->SetSchemaName(schemaName.c_str());

	if (NULL != classNames)
	{
		FdoPtr<FdoStringCollection> names = MgFeatureUtil::MgToFdoStringCollection(classNames, false);
		describe->SetClassNames(names);
	}

    Ptr<MgFeatureSchemaCollection> fsCollection = new MgFeatureSchemaCollection();

	FdoPtr<FdoFeatureSchemaCollection> ffsc = describe->Execute();
	
    // Get schema count
    FdoInt32 schemaCount = ffsc->GetCount();

    //
    // A new MgFeatureSchema needs to be created for each primary schema in FDO schemas.
    //

    Ptr<MgFeatureSchema> schema;
    Ptr<MgClassDefinitionCollection> classCol;

    for (int nSchemaIndex = 0; nSchemaIndex < schemaCount; nSchemaIndex++)
    {
        // Retrieve schema from the collection
        FdoPtr<FdoFeatureSchema> ffs = ffsc->GetItem(nSchemaIndex);
        STRING currSchemaName = (wchar_t*)ffs->GetName();

        // Check if this schema is from secondary source which will be prefixed with [ExtensionName][RelationName],
        // ie [ExtensionName][RelationName]SchemaName
        if (currSchemaName.find(L"[") == 0)
        {
            // Found a schema for secondary source, so just skip over it for now
            continue;
        }

        schema = new MgFeatureSchema();
        classCol = schema->GetClasses();

        // Set the schema name and description
        FdoStringP fSchemaName = ffs->GetName();
        schema->SetName(STRING(fSchemaName));

        FdoStringP fSchemaDesc = ffs->GetDescription();
        schema->SetDescription(STRING(fSchemaDesc));

        // Get all classes for the schema
        FdoPtr<FdoClassCollection> fcc = ffs->GetClasses();
        FdoInt32 classCount = fcc->GetCount();

        // Add the primary class definitions to the MgClassDefinitionCollection
        for (FdoInt32 nClassIndex = 0; nClassIndex < classCount; nClassIndex++)
        {
            FdoPtr<FdoClassDefinition> fc = fcc->GetItem(nClassIndex);

            FdoStringP qname = fc->GetQualifiedName();
            FdoStringP name = fc->GetName();

            if (name != NULL && qname != NULL)
            {
                Ptr<MgClassDefinition> classDefinition = MgFeatureUtil::GetMgClassDefinition(fc, serialize);
                classCol->Add(classDefinition);
            }
        }

        /*
        MdfModel::FeatureSource* featureSource = MgFdoConnectionUtil::GetFeatureSource(resource);
        CHECKNULL(featureSource, L"MgdFeatureService::DescribeSchema");
        MdfModel::ExtensionCollection* extensions = featureSource->GetExtensions();
        CHECKNULL(extensions, L"MgdFeatureService::DescribeSchema");
        int extensionCount = extensions->GetCount();

        for (int i = 0; i < extensionCount; i++)
        {
            Ptr<MgClassDefinition> extClassDefinition;
            FdoPtr<FdoClassDefinition> originalClassDef;

            MdfModel::Extension* extension = extensions->GetAt(i);
            CHECKNULL(extension, L"MgdFeatureService::DescribeSchema");

            // Get the extension name
            STRING extensionName = (STRING)extension->GetName();

            // get FeatureClass element - this tells us which class is extended (SchemaName:ClassName)
            STRING featureClass = (STRING)extension->GetFeatureClass();

            // Parse the schemaName from the className
            STRING primSchemaName, primClassName;
            MgUtil::ParseQualifiedClassName(featureClass, primSchemaName, primClassName);

            if (currSchemaName != primSchemaName)
            {
                continue;
            }

            // Cycle thru FDO schemas for schemaName.
            for (int nIndex = 0; nIndex < schemaCount; nIndex++)
            {
                FdoPtr<FdoFeatureSchema> ffs = ffsc->GetItem(nIndex);
                STRING currSchemaName = (wchar_t*)ffs->GetName();

                // Check if this schema is from secondary source
                if (currSchemaName.find(L"[") == 0)
                {
                    // Found a schema for secondary source, so just skip over it
                    continue;
                }

                // get the class collection for this schema
                FdoPtr<FdoClassCollection> fcc = ffs->GetClasses();
                FdoInt32 classCount = fcc->GetCount();

                for (int nClassIndex = 0; nClassIndex < classCount; nClassIndex++)
                {
                    originalClassDef = fcc->GetItem(nClassIndex);

                    STRING className = (wchar_t*)originalClassDef->GetName();
                    if (className == primClassName)
                    {
                        // get the class definition
                        extClassDefinition = MgFeatureUtil::GetMgClassDefinition(originalClassDef, serialize);
                        break;
                    }
                }

                break;
            }

            if (NULL == extClassDefinition)
            {
                continue;
            }

            CalculatedPropertyCollection* calcPropsColl = extension->GetCalculatedProperties();
            if (calcPropsColl != NULL && calcPropsColl->GetCount() != 0)
            {
                FdoPtr<FdoIdentifierCollection> idList = FdoIdentifierCollection::Create();
                for (int idx = 0; idx < calcPropsColl->GetCount(); idx++)
                {
                    CalculatedProperty* calcProp = calcPropsColl->GetAt(idx);
                    FdoPtr<FdoExpression> expressionCalc = FdoExpression::Parse(calcProp->GetExpression().c_str());
                    FdoPtr<FdoComputedIdentifier> idfCalc = FdoComputedIdentifier::Create(calcProp->GetName().c_str(), expressionCalc);
                    idList->Add(idfCalc);
                }

                Ptr<MgPropertyDefinitionCollection> mpdcLocal = extClassDefinition->GetProperties();
                for(int idx = 0; idx < calcPropsColl->GetCount(); idx++)
                {
                    CalculatedProperty* calcProp = calcPropsColl->GetAt(idx);
                    if (calcProp != NULL)
                    {
                        MdfString nameExpr = calcProp->GetName();
                        MdfString valueExpr = calcProp->GetExpression();
                        if (nameExpr.size() != 0 && valueExpr.size() != 0)
                        {
                            FdoPropertyType retPropType = FdoPropertyType_DataProperty;
                            FdoDataType retDataType = FdoDataType_Double;
                            FdoPtr<FdoExpression> expr = FdoExpression::Parse(valueExpr.c_str());
                            FdoPtr<FdoExpression> expandedExpression = FdoExpressionEngineCopyFilter::Copy(expr, idList);
                            FdoExpressionEngine::GetExpressionType(originalClassDef, expandedExpression, retPropType, retDataType);
                            if (retPropType == FdoPropertyType_DataProperty)
                            {
                                STRING namePropStr = STRING(nameExpr.c_str());
                                Ptr<MgDataPropertyDefinition> propDefExpr = new MgDataPropertyDefinition(namePropStr);
                                propDefExpr->SetDataType(MgFeatureUtil::GetMgPropertyType(retDataType));
                                propDefExpr->SetNullable(true);
                                propDefExpr->SetReadOnly(true);
                                propDefExpr->SetAutoGeneration(false);
                                mpdcLocal->Add(propDefExpr);
                            }
                        }
                    }
                }
            }
            //
            // Finished adding primary class properties to the extension class definition
            // Now add the secondary class properties
            //

            // Determine the number of secondary sources (AttributeRelates)
            MdfModel::AttributeRelateCollection* attributeRelates = extension->GetAttributeRelates();
            CHECKNULL(attributeRelates, L"MgDescribeSchema.DescribeSchema");
            int nAttributeRelateCount = attributeRelates->GetCount();

            for (int arIndex = 0; arIndex < nAttributeRelateCount; arIndex++)
            {
                // get the attribute relate
                MdfModel::AttributeRelate* attributeRelate = attributeRelates->GetAt(arIndex);
                CHECKNULL(attributeRelate, L"MgDescribeSchema.DescribeSchema");

                // Get the name of the secondary feature class (AttributeClass)
                STRING attributeClass = (STRING)attributeRelate->GetAttributeClass();

                // Parse the schema name from the class name;
                STRING secSchemaName, secClassName;
                MgUtil::ParseQualifiedClassName(attributeClass, secSchemaName, secClassName);

                // Get the relation name
                STRING relationName = (STRING)attributeRelate->GetName();

                // Get the attributeName delimiter ( if none specified, default will be "" (blank) )
                STRING attributeNameDelimiter = (STRING)attributeRelate->GetAttributeNameDelimiter();

                // Get the resource id of the secondary feature source
                STRING secondaryResourceId = (STRING)attributeRelate->GetResourceId();

                // Establish connection to provider for secondary feature source
                Ptr<MgResourceIdentifier> secondaryFeatureSource = new MgResourceIdentifier(secondaryResourceId);
                if (NULL != secondaryFeatureSource)
                {
                    FdoPtr<FdoFeatureSchemaCollection> ffsc2;
                    
                    // The reference to the FDO connection from the MgServerFeatureConnection object must be cleaned up before the parent object
                    // otherwise it leaves the FDO connection marked as still in use.
                    FdoPtr<FdoIConnection> fdoConn2 = MgFdoConnectionUtil::CreateConnection(secondaryFeatureSource);
                    // Get the schema collection for the secondary resource
                    FdoPtr<FdoIDescribeSchema> fdoCommand2  = (FdoIDescribeSchema*)fdoConn2->CreateCommand(FdoCommandType_DescribeSchema);
                    CHECKNULL((FdoIDescribeSchema*)fdoCommand2, L"MgDescribeSchema.DescribeSchema");

                    //if (!secSchemaName.empty())
                    //{
                    //    fdoCommand2->SetSchemaName(secSchemaName.c_str());
                    //}

                    //if (!secClassName.empty())
                    //{
                    //    FdoPtr<FdoStringCollection> fdoClassNames2 = FdoStringCollection::Create();

                    //    fdoClassNames2->Add(secClassName.c_str());
                    //    fdoCommand2->SetClassNames(fdoClassNames2.p);
                    //}

                    // Execute the command
                    ffsc2 = fdoCommand2->Execute();
                    CHECKNULL((FdoFeatureSchemaCollection*)ffsc2, L"MgdFeatureService::DescribeSchema");

                    int nSecSchemaCnt = (int)ffsc2->GetCount();

                    // cycle thru FdoFeatureSchemaCollection for secSchemaName
                    for (int nSecSchemaIndex = 0; nSecSchemaIndex < nSecSchemaCnt; nSecSchemaIndex++)
                    {
                        // retrieve the schema
                        FdoPtr<FdoFeatureSchema> ffs = ffsc2->GetItem(nSecSchemaIndex);
                        STRING fdoSchemaName = (wchar_t*)ffs->GetName();

                        if (fdoSchemaName != secSchemaName)
                        {
                            continue;
                        }

                        // get the class collection for schema
                        FdoPtr<FdoClassCollection> fcc = ffs->GetClasses();
                        FdoInt32 classCount = fcc->GetCount();

                        // cycle thru class collection for secClassName
                        for (int nClassIndex = 0; nClassIndex < classCount; nClassIndex++)
                        {
                            Ptr<MgPropertyDefinitionCollection> mpdc = extClassDefinition->GetProperties();

                            FdoPtr<FdoClassDefinition> fc = fcc->GetItem(nClassIndex);

                            STRING qualifiedName = (const wchar_t*)fc->GetQualifiedName();
                            STRING className = (wchar_t*)fc->GetName();

                            if (className != secClassName)
                            {
                                continue;
                            }

                            // Set the parent name for the secondary class definition
                            FdoPtr<FdoSchemaElement> parent = fc->GetParent();
                            if (!secSchemaName.empty())
                            {
                                parent->SetName(secSchemaName.c_str());
                            }

                            // get the secondary class definition
                            Ptr<MgClassDefinition> classDefinition = MgFeatureUtil::GetMgClassDefinition(fc, serialize);

                            // retrieve the secondary properties and prefix them with the relation name
                            Ptr<MgPropertyDefinitionCollection> mpdc2 = classDefinition->GetProperties();
                            INT32 mpdc2Count = mpdc2->GetCount();

                            // Prefix the secondary properties with relationName and add to the extClassDefinition
                            for (INT32 secPropIndex = 0; secPropIndex < mpdc2Count; secPropIndex++)
                            {
                                Ptr<MgPropertyDefinition> propDef = mpdc2->GetItem(secPropIndex);
                                if ( MgFeaturePropertyType::GeometricProperty != propDef->GetPropertyType()
                                    && MgFeaturePropertyType::RasterProperty != propDef->GetPropertyType() )
                                {
                                    STRING secPropName = propDef->GetName();
                                    secPropName = relationName + attributeNameDelimiter + secPropName;
                                    propDef->SetName(secPropName);
                                    mpdc->Add(propDef);
                                }
                            }

                            break;

                        }  // end loop thru secondary class collection

                        break;

                    }  // end loop thru secondary schemas

                }  // end if (NULL != secFeatureSource)

            }  // end loop thru all attribute relates (joins)

            if (!extensionName.empty())
            {
                extClassDefinition->SetName(extensionName);
            }

            // Add the extension class definition to the MgClassDefinitionCollection
            classCol->Add(extClassDefinition);

        }  // Repeat for all extensions
        */
        // Add the schema to the MgFeatureSchemaCollection
        fsCollection->Add(schema);

    }  // End loop thru all schemas

    return fsCollection.Detach();
}

MgFeatureSchemaCollection* MgdFeatureService::DescribeSchema(MgResourceIdentifier* resource,
                                                  CREFSTRING schemaName) 
{ 
	return DescribeSchema(resource, schemaName, NULL);
}

STRING MgdFeatureService::DescribeSchemaAsXml(MgResourceIdentifier* resource,
                                   CREFSTRING schemaName,
                                   MgStringCollection* classNames) 
{ 
	CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::DescribeSchemaAsXml");

	FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(resource);
	conn->Open();

	FdoPtr<FdoIDescribeSchema> describe = (FdoIDescribeSchema*)conn->CreateCommand(FdoCommandType_DescribeSchema);
	describe->SetSchemaName(schemaName.c_str());

	if (NULL != classNames)
	{
		FdoPtr<FdoStringCollection> names = MgFeatureUtil::MgToFdoStringCollection(classNames, false);
		describe->SetClassNames(names);
	}

	FdoPtr<FdoFeatureSchemaCollection> schemas = describe->Execute();
	return FdoSchemaToXml(schemas);
}

STRING MgdFeatureService::DescribeSchemaAsXml(MgResourceIdentifier* resource,
                                   CREFSTRING schemaName) 
{ 
	CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::DescribeSchemaAsXml");

	FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(resource);
	conn->Open();

	FdoPtr<FdoIDescribeSchema> describe = (FdoIDescribeSchema*)conn->CreateCommand(FdoCommandType_DescribeSchema);
	describe->SetSchemaName(schemaName.c_str());

	FdoPtr<FdoFeatureSchemaCollection> schemas = describe->Execute();
	return FdoSchemaToXml(schemas);
}

STRING MgdFeatureService::FdoSchemaToXml(FdoFeatureSchemaCollection* schemas)
{
	STRING serializedXml;

    MG_FEATURE_SERVICE_TRY()
    CHECKARGUMENTNULL(schemas, L"MgdFeatureService::FdoSchemaToXml");

    FdoIoMemoryStreamP fmis = FdoIoMemoryStream::Create();
    CHECKARGUMENTNULL((FdoIoMemoryStream*)fmis, L"MgdFeatureService::FdoSchemaToXml");

    // Write to memory stream
    schemas->WriteXml(fmis);
    fmis->Reset(); // TODO: We should not be calling reset here. A defect in FDO should be fixed.

    FdoInt64 len = fmis->GetLength();
    FdoByte *bytes = new FdoByte[(size_t)len];
    CHECKARGUMENTNULL(bytes, L"MgdFeatureService::FdoSchemaToXml");

    fmis->Read(bytes, (FdoSize)len);

    Ptr<MgByteSource> byteSource = new MgByteSource((BYTE_ARRAY_IN)bytes, (INT32)len);
    byteSource->SetMimeType(MgMimeType::Xml);
    Ptr<MgByteReader> byteReader = byteSource->GetReader();

    string out = MgUtil::GetTextFromReader(byteReader);
    serializedXml = MgUtil::MultiByteToWideChar(out);

    delete [] bytes;

    MG_FEATURE_SERVICE_CATCH_AND_THROW(L"MgdFeatureService::FdoSchemaToXml")

    return serializedXml;
}

STRING MgdFeatureService::SchemaToXml(MgFeatureSchemaCollection* schemas) 
{ 
    CHECKARGUMENTNULL(schemas, L"MgdFeatureService::SchemaToXml");
    STRING serializedXml;

    MG_FEATURE_SERVICE_TRY()

    FdoPtr<FdoFeatureSchemaCollection> fdoSchemaCol = MgFeatureUtil::GetFdoFeatureSchemaCollection(schemas);
    CHECKNULL(fdoSchemaCol, L"MgdFeatureService::SchemaToXml");

    FdoIoMemoryStreamP fmis = FdoIoMemoryStream::Create();
    CHECKNULL((FdoIoMemoryStream*)fmis, L"MgdFeatureService::SchemaToXml");

    // Write to memory stream
    fdoSchemaCol->WriteXml(fmis);
    fmis->Reset(); // TODO: We should not be calling reset here. A defect in FDO should be fixed.

    FdoInt64 len = fmis->GetLength();
    FdoByte *bytes = new FdoByte[(size_t)len];
    CHECKNULL(bytes, L"MgdFeatureService::SchemaToXml");

    fmis->Read(bytes, (FdoSize)len);

    Ptr<MgByteSource> byteSource = new MgByteSource((BYTE_ARRAY_IN)bytes, (INT32)len);
    byteSource->SetMimeType(MgMimeType::Xml);
    Ptr<MgByteReader> byteReader = byteSource->GetReader();

    string out = MgUtil::GetTextFromReader(byteReader);
    serializedXml = MgUtil::MultiByteToWideChar(out);

    delete [] bytes;

    MG_FEATURE_SERVICE_CATCH_AND_THROW(L"MgdFeatureService::SchemaToXml")

    return serializedXml;
}

MgFeatureSchemaCollection* MgdFeatureService::XmlToSchema(CREFSTRING xml) 
{ 
	Ptr<MgFeatureSchemaCollection> mgSchemaCol;

    MG_FEATURE_SERVICE_TRY()
    mgSchemaCol = new MgFeatureSchemaCollection();

    string mbString = MgUtil::WideCharToMultiByte(xml);

    size_t len = mbString.length();

    FdoByte* gisBytes = (FdoByte*) mbString.c_str();

    FdoPtr<FdoIoMemoryStream> stream = FdoIoMemoryStream::Create();
    stream->Write(gisBytes, (FdoSize)len);

    FdoPtr<FdoFeatureSchemaCollection> fdoSchemaCol = FdoFeatureSchemaCollection::Create((FdoSchemaElement*)NULL);
    stream->Reset();
    fdoSchemaCol->ReadXml(stream);

    // Get schema count
    FdoInt32 cnt = fdoSchemaCol->GetCount();
    for (FdoInt32 i = 0; i < cnt; i++)
    {
        FdoPtr<FdoFeatureSchema> fdoSchema = fdoSchemaCol->GetItem(i);
        FdoStringP name = fdoSchema->GetName();
        CHECKNULL(name, L"MgdFeatureService::XmlToSchema");

        FdoStringP description = fdoSchema->GetDescription();

        STRING tmpName(name);
        STRING tmpDesc(description);

        Ptr<MgFeatureSchema> mgSchema = new MgFeatureSchema(tmpName, tmpDesc);
        Ptr<MgClassDefinitionCollection> classCol = mgSchema->GetClasses();

        // Get all classes for a schema
        FdoPtr<FdoClassCollection> fdoClassCol = fdoSchema->GetClasses();
        FdoInt32 classCount = fdoClassCol->GetCount();

        for (FdoInt32 j = 0; j < classCount; j++)
        {
            FdoPtr<FdoClassDefinition> fdoClassDef = fdoClassCol->GetItem(j);
            // TODO: Should we return qualified or non-qualified name
            FdoStringP qname = fdoClassDef->GetQualifiedName();
            FdoStringP name = fdoClassDef->GetName();

            if (name != NULL && qname != NULL)
            {
                Ptr<MgClassDefinition> classDefinition = MgFeatureUtil::GetMgClassDefinition(fdoClassDef, true);
                classCol->Add(classDefinition);
            }
        }
        mgSchemaCol->Add(mgSchema);
    }

    MG_FEATURE_SERVICE_CATCH_AND_THROW(L"MgdFeatureService::XmlToSchema")

    return mgSchemaCol.Detach();
}

MgFeatureReader* MgdFeatureService::SelectFeatures(MgResourceIdentifier* resource,
                                        CREFSTRING className,
                                        MgFeatureQueryOptions* options) 
{ 
	return SelectFeatures(resource, className, options, L"");
}

MgFeatureReader* MgdFeatureService::SelectFeatures(MgResourceIdentifier* resource,
                                        CREFSTRING className,
                                        MgFeatureQueryOptions* options,
                                        CREFSTRING coordinateSystem) 
{ 
	return SelectFeaturesInternal(resource, className, options, coordinateSystem, false);
}

MgFeatureReader* MgdFeatureService::SelectFeaturesInternal(MgResourceIdentifier* resource,
														   CREFSTRING className,
														   MgFeatureQueryOptions* options,
														   CREFSTRING coordinateSystem,
														   bool withLock)
{
	CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::SelectFeatures");
	CHECKARGUMENTNULL(options, L"MgdFeatureService::SelectFeatures");
    if (className.empty())
        throw new MgInvalidArgumentException(L"MgdFeatureService::SelectFeatures", __LINE__, __WFILE__, NULL, L"", NULL);

	FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(resource);
	conn->Open();

	Ptr<MgFeatureReader> reader;

	FdoPtr<FdoISelect> select = (FdoISelect*)conn->CreateCommand(FdoCommandType_Select);
	select->SetFeatureClassName(className.c_str());

	STRING filter = options->GetFilter();
	if (!filter.empty())
		select->SetFilter(filter.c_str());

	Ptr<MgStringCollection> props = options->GetClassProperties();
	Ptr<MgStringPropertyCollection> computed = options->GetComputedProperties();

	INT32 propCount = props->GetCount();
	INT32 compCount = computed->GetCount();

	FdoPtr<FdoIdentifierCollection> fdoProps = select->GetPropertyNames();
	if (propCount > 0)
	{
		for (INT32 i = 0; i < propCount; i++)
		{
			FdoPtr<FdoIdentifier> name = FdoIdentifier::Create(props->GetItem(i).c_str());
			fdoProps->Add(name);
		}
	}
	if (compCount > 0)
	{
		for (INT32 i = 0; i < compCount; i++)
		{
			Ptr<MgStringProperty> comp = computed->GetItem(i);
			FdoPtr<FdoExpression> expr = FdoExpression::Parse(comp->GetValue().c_str());
			FdoPtr<FdoComputedIdentifier> name= FdoComputedIdentifier::Create(comp->GetName().c_str(), expr);

			fdoProps->Add(name);
		}
	}

	FdoPtr<FdoIFeatureReader> fdoReader;
	if (withLock)
		fdoReader = select->ExecuteWithLock();
	else
		fdoReader = select->Execute();
    
    //If coordinate system specified, set up a CS Transform to pass to the feature reader
    Ptr<MgCoordinateSystemTransform> xform;
    if (!coordinateSystem.empty())
    {
        FdoPtr<FdoFeatureClass> clsDef = dynamic_cast<FdoFeatureClass*>(fdoReader->GetClassDefinition());
        if (NULL != clsDef)
        {
            FdoPtr<FdoGeometricPropertyDefinition> geom = clsDef->GetGeometryProperty();
            if (NULL != geom)
            {
                FdoString* scName = geom->GetSpatialContextAssociation();
                FdoPtr<FdoIGetSpatialContexts> gsc = (FdoIGetSpatialContexts*)conn->CreateCommand(FdoCommandType_GetSpatialContexts);
                FdoPtr<FdoISpatialContextReader> scReader = gsc->Execute();
                while (scReader->ReadNext())
                {
                    if (wcscmp(scReader->GetName(), scName) == 0)
                    {
                        //TODO: Some FDO providers don't even use WKTs for spatial context. Capabilities
                        //should tell us the correct way to construct our MgCoordinateSystem object

                        STRING wkt = scReader->GetCoordinateSystemWkt();

                        Ptr<MgCoordinateSystemFactory> fact = new MgCoordinateSystemFactory();

                        Ptr<MgCoordinateSystem> source = fact->Create(wkt);
                        Ptr<MgCoordinateSystem> target = fact->Create(coordinateSystem);

                        xform = fact->GetTransform(source, target);
                        break;
                    }
                }
            }
        }
    }
    
    if (NULL == xform)
    {
	    reader = new MgdFeatureReader(fdoReader);
    }
    else
    {
        NOT_IMPLEMENTED(L"MgdFeatureService::SelectFeaturesInternal");
    }

	return reader.Detach();
}

MgDataReader* MgdFeatureService::SelectAggregate(MgResourceIdentifier* resource,
                                      CREFSTRING className,
                                      MgFeatureAggregateOptions* options) 
{ 
	CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::SelectAggregate");
	CHECKARGUMENTNULL(options, L"MgdFeatureService::SelectAggregate");
    if (className.empty())
        throw new MgInvalidArgumentException(L"MgdFeatureService::SelectAggregate", __LINE__, __WFILE__, NULL, L"", NULL);

	FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(resource);
	conn->Open();

	Ptr<MgDataReader> reader;

	FdoPtr<FdoISelectAggregates> select = (FdoISelectAggregates*)conn->CreateCommand(FdoCommandType_SelectAggregates);
	select->SetFeatureClassName(className.c_str());

	STRING filter = options->GetFilter();
	if (!filter.empty())
		select->SetFilter(filter.c_str());

	Ptr<MgStringCollection> props = options->GetClassProperties();
	Ptr<MgStringPropertyCollection> computed = options->GetComputedProperties();

	INT32 propCount = props->GetCount();
	INT32 compCount = computed->GetCount();

	FdoPtr<FdoIdentifierCollection> fdoProps = select->GetPropertyNames();
	if (propCount > 0)
	{
		for (INT32 i = 0; i < propCount; i++)
		{
			FdoPtr<FdoIdentifier> name = FdoIdentifier::Create(props->GetItem(i).c_str());
			fdoProps->Add(name);
		}
	}
	if (compCount > 0)
	{
		for (INT32 i = 0; i < compCount; i++)
		{
			Ptr<MgStringProperty> comp = computed->GetItem(i);
			FdoPtr<FdoExpression> expr = FdoExpression::Parse(comp->GetValue().c_str());
			FdoPtr<FdoComputedIdentifier> name= FdoComputedIdentifier::Create(comp->GetName().c_str(), expr);

			fdoProps->Add(name);
		}
	}

	if (options->GetDistinct())
		select->SetDistinct(true);

	STRING groupFilter = options->GetGroupFilter();
	if (!groupFilter.empty())
	{
		FdoPtr<FdoFilter> grpFilter = FdoFilter::Parse(groupFilter.c_str());
		select->SetGroupingFilter(grpFilter);
	}
	FdoPtr<FdoIdentifierCollection> fdoGroupBy = select->GetGrouping();
	Ptr<MgStringCollection> mgGroupBy = options->GetGroupingProperties();
	if (NULL != mgGroupBy && mgGroupBy->GetCount() > 0)
	{
		for (INT32 i = 0; i < mgGroupBy->GetCount(); i++)
		{
			STRING name = mgGroupBy->GetItem(i);
			FdoPtr<FdoIdentifier> fdoId = FdoIdentifier::Create(name.c_str());

			fdoGroupBy->Add(fdoId);
		}
	}

	FdoPtr<FdoIDataReader> fdoReader = select->Execute();
	reader = new MgdDataReader(fdoReader);

	return reader.Detach();
}

MgPropertyCollection* MgdFeatureService::UpdateFeatures(MgResourceIdentifier* resource,
                                             MgFeatureCommandCollection* commands,
                                             bool useTransaction) 
{ 
	CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::UpdateFeatures");
    CHECKARGUMENTNULL(commands, L"MgdFeatureService::UpdateFeatures");

    Ptr<MgPropertyCollection> result;
    Ptr<MgTransaction> trans;
    if (useTransaction)
    {
        try 
        {
            trans = BeginTransaction(resource);
        }
        catch (MgException* ex) 
        {
            SAFE_RELEASE(ex);
        }
        catch (FdoException* ex)
        {
            FDO_SAFE_RELEASE(ex);
        }
    }

    result = UpdateFeatures(resource, commands, trans);
    if (useTransaction && NULL != trans)
    {
        trans->Commit();
    }
    return result.Detach();
}

MgPropertyCollection* MgdFeatureService::UpdateFeatures(MgResourceIdentifier* resource,
                                             MgFeatureCommandCollection* commands,
                                             MgTransaction* transaction) 
{ 
	CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::UpdateFeatures");
    CHECKARGUMENTNULL(commands, L"MgdFeatureService::UpdateFeatures");
    CHECKARGUMENTNULL(transaction, L"MgdFeatureService::UpdateFeatures");

    Ptr<MgPropertyCollection> ret;

	FdoPtr<FdoIConnection> conn;
    Ptr<MgdTransaction> mgTrans = dynamic_cast<MgdTransaction*>(transaction);
    if (NULL != mgTrans)
    {
        SAFE_ADDREF(mgTrans.p);
        Ptr<MgResourceIdentifier> origFeatureSource = mgTrans->GetFeatureSource();
        //Check that the transaction originates from the same feature source
        if (origFeatureSource->ToString() != resource->ToString())
            throw new MgInvalidArgumentException(L"MgdFeatureService::InsertFeatures", __LINE__, __WFILE__, NULL, L"", NULL);

        conn = mgTrans->GetFdoConnection(); //Connection is already open
    }
    else
    {    
        conn = MgFdoConnectionUtil::CreateConnection(resource);
	    conn->Open();
    }

    ret = new MgPropertyCollection();

    INT32 cmdCount = commands->GetCount();
    if (cmdCount == 0)
    {
        MgStringCollection arguments;
        arguments.Add(L"2");
        arguments.Add(L"0");

        throw new MgInvalidArgumentException(L"MgdFeatureService::UpdateFeatures",
            __LINE__, __WFILE__, &arguments, L"MgCollectionEmpty", NULL);
    }

    FdoPtr<FdoICommandCapabilities> cmdCaps = conn->GetCommandCapabilities();
    FdoInt32 sCmdCount;
    FdoInt32* supportedCmds = cmdCaps->GetCommands(sCmdCount);

    for (INT32 i = 0; i < cmdCount; i++)
    {
        Ptr<MgFeatureCommand> fc = commands->GetItem(i);
        INT32 cmdType = fc->GetCommandType();
        switch(cmdType)
        {
        case MgFeatureCommandType::InsertFeatures:
            {
                bool supports = false;
                for (FdoInt32 j = 0; j < sCmdCount; j++)
                {
                    if (supportedCmds[i] == FdoCommandType_Insert)
                    {
                        supports = true;
                        break;
                    }
                }
                if (!supports)
                {
                    STRING message = MgFeatureUtil::GetMessage(L"MgCommandNotSupported");
                    MgStringCollection arguments;
                    arguments.Add(message);
                    throw new MgFeatureServiceException(L"MgdFeatureService::UpdateFeatures", __LINE__, __WFILE__, &arguments, L"", NULL);
                }

                Ptr<MgInsertFeatures> insert = dynamic_cast<MgInsertFeatures*>(fc.p);
                SAFE_ADDREF(insert.p);

                Ptr<MgPropertyCollection> properties = insert->GetPropertyValues();
                Ptr<MgFeatureReader> reader = InsertFeatures(resource, insert->GetFeatureClassName(), properties);

                Ptr<MgFeatureProperty> result = new MgFeatureProperty(L"InsertResult", reader);
                ret->Add(result);
            }
            break;
        case MgFeatureCommandType::DeleteFeatures:
            {
                bool supports = false;
                for (FdoInt32 j = 0; j < sCmdCount; j++)
                {
                    if (supportedCmds[i] == FdoCommandType_Delete)
                    {
                        supports = true;
                        break;
                    }
                }
                if (!supports)
                {
                    STRING message = MgFeatureUtil::GetMessage(L"MgCommandNotSupported");
                    MgStringCollection arguments;
                    arguments.Add(message);
                    throw new MgFeatureServiceException(L"MgdFeatureService::UpdateFeatures", __LINE__, __WFILE__, &arguments, L"", NULL);
                }

                Ptr<MgDeleteFeatures> deleteCmd = dynamic_cast<MgDeleteFeatures*>(fc.p);
                SAFE_ADDREF(deleteCmd.p);

                int deleted = DeleteFeatures(resource, deleteCmd->GetFeatureClassName(), deleteCmd->GetFilterText());

                Ptr<MgInt32Property> result = new MgInt32Property(L"DeleteResult", deleted);
                ret->Add(result);
            }
            break;
        case MgFeatureCommandType::UpdateFeatures:
            {
                bool supports = false;
                for (FdoInt32 j = 0; j < sCmdCount; j++)
                {
                    if (supportedCmds[i] == FdoCommandType_Update)
                    {
                        supports = true;
                        break;
                    }
                }
                if (!supports)
                {
                    STRING message = MgFeatureUtil::GetMessage(L"MgCommandNotSupported");
                    MgStringCollection arguments;
                    arguments.Add(message);
                    throw new MgFeatureServiceException(L"MgdFeatureService::UpdateFeatures", __LINE__, __WFILE__, &arguments, L"", NULL);
                }

                Ptr<MgUpdateFeatures> update = dynamic_cast<MgUpdateFeatures*>(fc.p);
                SAFE_ADDREF(update.p);

                Ptr<MgPropertyCollection> properties = update->GetPropertyValues();
                int updated = UpdateFeatures(resource, update->GetFeatureClassName(), properties, update->GetFilterText());

                Ptr<MgInt32Property> result = new MgInt32Property(L"UpdateResult", updated);
                ret->Add(result);
            }
            break;
        default:
            STRING message = MgFeatureUtil::GetMessage(L"MgCommandNotSupported");
            MgStringCollection arguments;
            arguments.Add(message);
            throw new MgFeatureServiceException(L"MgdFeatureService::UpdateFeatures", __LINE__, __WFILE__, &arguments, L"", NULL);
        }
    }

    return ret.Detach();
}


MgFeatureReader* MgdFeatureService::InsertFeatures(MgResourceIdentifier* resource, CREFSTRING className, MgPropertyCollection* propertyValues)
{
	return InsertFeatures(resource, className, propertyValues, NULL);
}

MgFeatureReader* MgdFeatureService::InsertFeatures(MgResourceIdentifier* resource, CREFSTRING className, MgPropertyCollection* propertyValues, MgTransaction* trans)
{
    CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::InsertFeatures");
	CHECKARGUMENTNULL(propertyValues, L"MgdFeatureService::InsertFeatures");
	if (className.empty())
		throw new MgNullArgumentException(L"MgdFeatureService::InsertFeatures", __LINE__, __WFILE__, NULL, L"", NULL);
	
	Ptr<MgFeatureReader> reader;
	
	FdoPtr<FdoIConnection> conn;
    Ptr<MgdTransaction> mgTrans = dynamic_cast<MgdTransaction*>(trans);
    if (NULL != mgTrans)
    {
        SAFE_ADDREF(mgTrans.p);
        Ptr<MgResourceIdentifier> origFeatureSource = mgTrans->GetFeatureSource();
        //Check that the transaction originates from the same feature source
        if (origFeatureSource->ToString() != resource->ToString())
            throw new MgInvalidArgumentException(L"MgdFeatureService::InsertFeatures", __LINE__, __WFILE__, NULL, L"", NULL);

        conn = mgTrans->GetFdoConnection(); //Connection is already open
    }
    else
    {    
        conn = MgFdoConnectionUtil::CreateConnection(resource);
	    conn->Open();
    }

	FdoPtr<FdoIInsert> insert = (FdoIInsert*)conn->CreateCommand(FdoCommandType_Insert);
	
	insert->SetFeatureClassName(className.c_str());

	FdoPtr<FdoPropertyValueCollection> propVals = insert->GetPropertyValues();
	for (INT32 i = 0; i < propertyValues->GetCount(); i++)
	{
		Ptr<MgProperty> mgp = propertyValues->GetItem(i);
		FdoPtr<FdoPropertyValue> pv = MgFeatureUtil::MgPropertyToFdoProperty(mgp);

		propVals->Add(pv);
	}

	FdoPtr<FdoIFeatureReader> insertRes = insert->Execute();

	reader = new MgdFeatureReader(insertRes);
	return reader.Detach();
}

int MgdFeatureService::UpdateFeatures(MgResourceIdentifier* resource, CREFSTRING className, MgPropertyCollection* propertyValues, CREFSTRING filter)
{
	return UpdateFeatures(resource, className, propertyValues, filter, NULL);
}

int MgdFeatureService::UpdateFeatures(MgResourceIdentifier* resource, CREFSTRING className, MgPropertyCollection* propertyValues, CREFSTRING filter, MgTransaction* trans)
{
    CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::UpdateFeatures");
	CHECKARGUMENTNULL(propertyValues, L"MgdFeatureService::UpdateFeatures");
	if (className.empty())
		throw new MgNullArgumentException(L"MgdFeatureService::UpdateFeatures", __LINE__, __WFILE__, NULL, L"", NULL);

	int updated = 0;

	FdoPtr<FdoIConnection> conn;
    Ptr<MgdTransaction> mgTrans = dynamic_cast<MgdTransaction*>(trans);
    if (NULL != mgTrans)
    {
        SAFE_ADDREF(mgTrans.p);
        Ptr<MgResourceIdentifier> origFeatureSource = mgTrans->GetFeatureSource();
        //Check that the transaction originates from the same feature source
        if (origFeatureSource->ToString() != resource->ToString())
            throw new MgInvalidArgumentException(L"MgdFeatureService::UpdateFeatures", __LINE__, __WFILE__, NULL, L"", NULL);

        conn = mgTrans->GetFdoConnection(); //Connection is already open
    }
    else
    {
        conn = MgFdoConnectionUtil::CreateConnection(resource);
	    conn->Open();
    }

	FdoPtr<FdoIUpdate> update = (FdoIUpdate*)conn->CreateCommand(FdoCommandType_Update);
	update->SetFeatureClassName(className.c_str());
	
	if (!filter.empty())
		update->SetFilter(filter.c_str());

	FdoPtr<FdoPropertyValueCollection> propVals = update->GetPropertyValues();
	for (INT32 i = 0; i < propertyValues->GetCount(); i++)
	{
		Ptr<MgProperty> mgp = propertyValues->GetItem(i);
		FdoPtr<FdoPropertyValue> pv = MgFeatureUtil::MgPropertyToFdoProperty(mgp);

		propVals->Add(pv);
	}

	updated = update->Execute();

	return updated;
}

int MgdFeatureService::DeleteFeatures(MgResourceIdentifier* resource, CREFSTRING className, CREFSTRING filter)
{
	return DeleteFeatures(resource, className, filter, NULL);
}

int MgdFeatureService::DeleteFeatures(MgResourceIdentifier* resource, CREFSTRING className, CREFSTRING filter, MgTransaction* trans)
{
    CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::DeleteFeatures");
	if (className.empty())
		throw new MgNullArgumentException(L"MgdFeatureService::DeleteFeatures", __LINE__, __WFILE__, NULL, L"", NULL);

    int deleted = 0;

	FdoPtr<FdoIConnection> conn;

    Ptr<MgdTransaction> mgTrans = dynamic_cast<MgdTransaction*>(trans);
    if (NULL != mgTrans)
    {
        SAFE_ADDREF(mgTrans.p);
        Ptr<MgResourceIdentifier> origFeatureSource = mgTrans->GetFeatureSource();
        //Check that the transaction originates from the same feature source
        if (origFeatureSource->ToString() != resource->ToString())
            throw new MgInvalidArgumentException(L"MgdFeatureService::DeleteFeatures", __LINE__, __WFILE__, NULL, L"", NULL);

        conn = mgTrans->GetFdoConnection(); //Connection is already open
    }
    else
    {
        conn = MgFdoConnectionUtil::CreateConnection(resource);
	    conn->Open();
    }

	FdoPtr<FdoIDelete> fdoDelete = (FdoIDelete*)conn->CreateCommand(FdoCommandType_Delete);
	fdoDelete->SetFeatureClassName(className.c_str());

	fdoDelete->SetFilter(filter.c_str());

	deleted = fdoDelete->Execute();

	return deleted;
}

MgFeatureReader* MgdFeatureService::GetLockedFeatures(MgResourceIdentifier* resource,
                                           CREFSTRING className,
                                           MgFeatureQueryOptions* options) 
{ 
	return SelectFeaturesInternal(resource, className, options, L"", true);
}

MgTransaction* MgdFeatureService::BeginTransaction(MgResourceIdentifier* resource) 
{ 
	CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::BeginTransaction");

    FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(resource);
    conn->Open();

    return new MgdTransaction(conn, resource);
}

MgSqlDataReader* MgdFeatureService::ExecuteSqlQuery(MgResourceIdentifier* resource,
                                         CREFSTRING sqlStatement) 
{ 
	CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::ExecuteSqlQuery");
    if (sqlStatement.empty())
        throw new MgInvalidArgumentException(L"MgdFeatureService::ExecuteSqlQuery", __LINE__, __WFILE__, NULL, L"", NULL);

	Ptr<MgSqlDataReader> reader;
	FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(resource);
	conn->Open();

	FdoPtr<FdoISQLCommand> sql = (FdoISQLCommand*)conn->CreateCommand(FdoCommandType_SQLCommand);
	sql->SetSQLStatement(sqlStatement.c_str());

	FdoPtr<FdoISQLDataReader> fdoReader = sql->ExecuteReader();
	reader = new MgdSqlDataReader(fdoReader);

	return reader.Detach();
}

MgSqlDataReader* MgdFeatureService::ExecuteSqlQuery(MgResourceIdentifier* resource,
                                         CREFSTRING sqlStatement,
                                         MgParameterCollection* parameters,
                                         MgTransaction* transaction) 
{ 
	CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::ExecuteSqlQuery");
    if (sqlStatement.empty())
        throw new MgInvalidArgumentException(L"MgdFeatureService::ExecuteSqlQuery", __LINE__, __WFILE__, NULL, L"", NULL);

    CHECKARGUMENTNULL(parameters, L"MgdFeatureService::ExecuteSqlQuery");
    CHECKARGUMENTNULL(transaction, L"MgdFeatureService::ExecuteSqlQuery");

	NOT_IMPLEMENTED(L"MgdFeatureService::ExecuteSqlQuery");
}

INT32 MgdFeatureService::ExecuteSqlNonQuery(MgResourceIdentifier* resource,
                                 CREFSTRING sqlNonSelectStatement) 
{ 
	CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::ExecuteSqlNonQuery");
    if (sqlNonSelectStatement.empty())
        throw new MgInvalidArgumentException(L"MgdFeatureService::ExecuteSqlNonQuery", __LINE__, __WFILE__, NULL, L"", NULL);

	FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(resource);
	conn->Open();

	FdoPtr<FdoISQLCommand> sql = (FdoISQLCommand*)conn->CreateCommand(FdoCommandType_SQLCommand);
	sql->SetSQLStatement(sqlNonSelectStatement.c_str());

	return sql->ExecuteNonQuery();
}

INT32 MgdFeatureService::ExecuteSqlNonQuery(MgResourceIdentifier* resource,
                                 CREFSTRING sqlNonSelectStatement,
                                 MgParameterCollection* parameters,
                                 MgTransaction* transaction)
{ 
    CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::ExecuteSqlNonQuery");
    if (sqlNonSelectStatement.empty())
        throw new MgInvalidArgumentException(L"MgdFeatureService::ExecuteSqlNonQuery", __LINE__, __WFILE__, NULL, L"", NULL);

    CHECKARGUMENTNULL(parameters, L"MgdFeatureService::ExecuteSqlQuery");
    CHECKARGUMENTNULL(transaction, L"MgdFeatureService::ExecuteSqlQuery");

	NOT_IMPLEMENTED(L"MgdFeatureService::ExecuteSqlNonQuery");
}

MgSpatialContextReader* MgdFeatureService::GetSpatialContexts(MgResourceIdentifier* resource,
                                                   bool bActiveOnly) 
{
	CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::GetSpatialContexts");
	Ptr<MgSpatialContextReader> reader;

	FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(resource);
    conn->Open();

	FdoPtr<FdoIGetSpatialContexts> list = (FdoIGetSpatialContexts*)conn->CreateCommand(FdoCommandType_GetSpatialContexts);
	list->SetActiveOnly(bActiveOnly);

	FdoPtr<FdoISpatialContextReader> scReader = list->Execute();
	reader = new MgSpatialContextReader();

	//TODO: Consider overrides from feature source

	while (scReader->ReadNext())
	{
		Ptr<MgSpatialContextData> data = new MgSpatialContextData();
		data->SetActiveStatus(scReader->IsActive());
		data->SetCoordinateSystem(STRING(scReader->GetCoordinateSystem()));
		data->SetCoordinateSystemWkt(STRING(scReader->GetCoordinateSystemWkt()));
		data->SetDescription(STRING(scReader->GetDescription()));

		data->SetExtentType((INT32)scReader->GetExtentType());
		FdoPtr<FdoByteArray> ext = scReader->GetExtent();
		if (ext.p != NULL)
		{
			INT32 size = (INT32)ext->GetCount();
			BYTE_ARRAY_IN bytes = (BYTE_ARRAY_IN)ext->GetData();
			Ptr<MgByte> extent = new MgByte(bytes, size);
			data->SetExtent(extent);
		}

		data->SetName(STRING(scReader->GetName()));
		data->SetXYTolerance(scReader->GetXYTolerance());
		data->SetZTolerance(scReader->GetZTolerance());

		reader->AddSpatialData(data);
	}

	return reader.Detach();
}

MgLongTransactionReader* MgdFeatureService::GetLongTransactions(MgResourceIdentifier* resource,
                                                     bool bActiveOnly) 
{ 
	CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::GetLongTransactions");
	Ptr<MgLongTransactionReader> reader;

	FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(resource);
	conn->Open();

	FdoPtr<FdoIGetLongTransactions> list = (FdoIGetLongTransactions*)conn->CreateCommand(FdoCommandType_GetLongTransactions);
	FdoPtr<FdoILongTransactionReader> ltReader = list->Execute();

	reader = new MgLongTransactionReader();

	while (ltReader->ReadNext())
	{
		Ptr<MgLongTransactionData> data = new MgLongTransactionData();
		data->SetActiveStatus(ltReader->IsActive());
		FdoDateTime fdt = ltReader->GetCreationDate();
		data->SetCreationDate(new MgDateTime(fdt.year, fdt.month, fdt.day, fdt.hour, fdt.minute, fdt.seconds, 0));
		data->SetDescription(ltReader->GetDescription());
		data->SetFrozenStatus(ltReader->IsFrozen());
		data->SetName(ltReader->GetName());
		data->SetOwner(ltReader->GetOwner());
	
		reader->AddLongTransactionData(data);
	}
	ltReader->Close();

	return reader.Detach();
}

bool MgdFeatureService::SetLongTransaction(MgResourceIdentifier* resource,
                                CREFSTRING longTransactionName) 
{ 
	CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::SetLongTransaction");
    if (longTransactionName.empty())
        throw new MgNullArgumentException(L"MgdFeatureService::SetLongTransaction", __LINE__, __WFILE__, NULL, L"", NULL);

	bool ok = false;
	try 
	{
		FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(resource);
		FdoPtr<FdoIActivateLongTransaction> activate = (FdoIActivateLongTransaction*)conn->CreateCommand(FdoCommandType_ActivateLongTransaction);
		activate->SetName(longTransactionName.c_str());
		activate->Execute();
		ok = true;
	}
	catch (FdoException* ex)
	{
		ex->Release();
		ok = false;
	}

	return ok;
}

bool MgdFeatureService::SupportsPartialSchemaDiscovery(FdoIConnection* conn)
{
	CHECKARGUMENTNULL(conn, L"MgdFeatureService::SupportsPartialSchemaDiscovery");
	FdoPtr<FdoICommandCapabilities> caps = conn->GetCommandCapabilities();

	bool getClassNames = false;
	bool getSchemaNames = false;

	FdoInt32 cmdCount;
	FdoInt32* cmds = caps->GetCommands(cmdCount);
	for (FdoInt32 i = 0; i < cmdCount; i++)
	{
		if (cmds[i] == FdoCommandType_GetClassNames)
			getClassNames = true;
		else if (cmds[i] == FdoCommandType_GetSchemaNames)
			getSchemaNames = true;
	}

	return getClassNames && getSchemaNames;
}

MgStringCollection* MgdFeatureService::GetSchemas(MgResourceIdentifier* resource) 
{ 
	CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::GetSchemas");

	FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(resource);
	conn->Open();

	if (SupportsPartialSchemaDiscovery(conn))
	{
		FdoPtr<FdoIGetSchemaNames> fetch = (FdoIGetSchemaNames*)conn->CreateCommand(FdoCommandType_GetSchemaNames);
		FdoPtr<FdoStringCollection> names = fetch->Execute();

		return MgFeatureUtil::FdoToMgStringCollection(names, false);
	}
	else
	{
		Ptr<MgStringCollection> names = new MgStringCollection();
		FdoPtr<FdoIDescribeSchema> describe = (FdoIDescribeSchema*)conn->CreateCommand(FdoCommandType_DescribeSchema);
		FdoPtr<FdoFeatureSchemaCollection> schemas = describe->Execute();

		for (INT32 i = 0; i < schemas->GetCount(); i++)
		{
			FdoPtr<FdoFeatureSchema> schema = schemas->GetItem(i);
			STRING name = schema->GetName();
			names->Add(name);
		}

		return names.Detach();
	}
}

MgStringCollection* MgdFeatureService::GetClasses(MgResourceIdentifier* resource, CREFSTRING schemaName) 
{ 
	CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::GetClasses");
	if (schemaName.empty())
		throw new MgInvalidArgumentException(L"MgdFeatureService::GetClasses", __LINE__, __WFILE__, NULL, L"", NULL);

	FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(resource);
	conn->Open();

	if (SupportsPartialSchemaDiscovery(conn))
	{
		FdoPtr<FdoIGetClassNames> fetch = (FdoIGetClassNames*)conn->CreateCommand(FdoCommandType_GetClassNames);
		fetch->SetSchemaName(schemaName.c_str());
		FdoPtr<FdoStringCollection> names = fetch->Execute();

		return MgFeatureUtil::FdoToMgStringCollection(names, false);
	}
	else
	{
		Ptr<MgStringCollection> names = new MgStringCollection();
		FdoPtr<FdoIDescribeSchema> describe = (FdoIDescribeSchema*)conn->CreateCommand(FdoCommandType_DescribeSchema);
		FdoPtr<FdoFeatureSchemaCollection> schemas = describe->Execute();

		for (INT32 i = 0; i < schemas->GetCount(); i++)
		{
			FdoPtr<FdoFeatureSchema> schema = schemas->GetItem(i);
			STRING name = schema->GetName();
			if (name != schemaName)
				continue;

			FdoPtr<FdoClassCollection> classes = schema->GetClasses();

			for (INT32 j = 0; j < classes->GetCount(); j++)
			{
				FdoPtr<FdoClassDefinition> cls = classes->GetItem(j);
				STRING clsName = cls->GetName();
				names->Add(clsName);
			}
		}

		return names.Detach();
	}
}

MgClassDefinition* MgdFeatureService::GetClassDefinition(MgResourceIdentifier* resource,
                                              CREFSTRING schemaName,
                                              CREFSTRING className) 
{ 
	return GetClassDefinition(resource, schemaName, className, true);
}

//Though internal (and thus, normally wouldn't be implemented), it's needed by MgMappingUtil
MgClassDefinition* MgdFeatureService::GetClassDefinition(MgResourceIdentifier* resource,
                                              CREFSTRING schemaName,
                                              CREFSTRING className,
                                              bool serialize) 
{
    CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::GetClassDefinition");
	if (schemaName.empty())
		throw new MgInvalidArgumentException(L"MgdFeatureService::GetClassDefinition", __LINE__, __WFILE__, NULL, L"", NULL);
	if (className.empty())
		throw new MgInvalidArgumentException(L"MgdFeatureService::GetClassDefinition", __LINE__, __WFILE__, NULL, L"", NULL);

	Ptr<MgClassDefinition> clsDef;

    MgStringCollection clsNames;
    clsNames.Add(className);
    Ptr<MgFeatureSchemaCollection> mgSchemas;
    
    try 
    {
        mgSchemas = DescribeSchema(resource, schemaName, &clsNames, serialize);
    }
    catch(FdoException* ex) //Bad schema name
    {
        ex->Release();
        throw new MgClassNotFoundException(L"MgdFeatureService::GetClassDefinition", __LINE__, __WFILE__, NULL, L"", NULL);
    }

    INT32 sc = mgSchemas->GetCount();
    for (INT32 i = 0; i < sc; i++)
    {
        Ptr<MgFeatureSchema> mfs = mgSchemas->GetItem(i);
        if (mfs->GetName() == schemaName)
        {
            Ptr<MgClassDefinitionCollection> classes = mfs->GetClasses();
            INT32 cc = classes->GetCount();

            for (INT32 j = 0; j < cc; j++)
            {
                Ptr<MgClassDefinition> cls = classes->GetItem(i);
                if (cls->GetName() == className)
                {
                    clsDef = cls;
                    goto done; //So sue me!
                }
            }
        }
    }

    if (NULL == clsDef.p)
        throw new MgClassNotFoundException(L"MgdFeatureService::GetClassDefinition", __LINE__, __WFILE__, NULL, L"", NULL);

done:
	return clsDef.Detach();
}

void MgdFeatureService::CreateFeatureSource(MgResourceIdentifier* resource, MgFeatureSourceParams* sourceParams) 
{ 
    CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::CreateFeatureSource");
    CHECKARGUMENTNULL(sourceParams, L"MgdFeatureService::CreateFeatureSource");

    MG_FEATURE_SERVICE_TRY()

    MgFileFeatureSourceParams* params = dynamic_cast<MgFileFeatureSourceParams*>(sourceParams);
    if (NULL != params)
    {
        Ptr<MgCreateFileFeatureSource> creator = NULL;
        STRING providerName = params->GetProviderName();
        if (providerName == L"OSGeo.SDF") // NOXLATE
        {
            creator = new MgCreateSdfFeatureSource(resource, params);
            creator->CreateFeatureSource(false, false);
        }
        else if (providerName == L"OSGeo.SHP") // NOXLATE
        {
            creator = new MgCreateShpFeatureSource(resource, params);
            creator->CreateFeatureSource(true, false);
        }
        else if (providerName == L"OSGeo.SQLite") // NOXLATE
        {
            creator = new MgCreateSqliteFeatureSource(resource, params);
            creator->CreateFeatureSource(false, false);
        }
        else
            throw new MgInvalidArgumentException(L"MgCreateFeatureSource.CreateFeatureSource", __LINE__, __WFILE__, NULL, L"", NULL);
    }

    MG_FEATURE_SERVICE_CHECK_CONNECTION_CATCH_AND_THROW(resource, L"MgCreateFeatureSource.CreateFeatureSource")
}

MgByteReader* MgdFeatureService::DescribeWfsFeatureType(MgResourceIdentifier* featureSourceId,
                                             MgStringCollection* featureClasses) 
{ 
	NOT_IMPLEMENTED(L"MgdFeatureService::DescribeWfsFeatureType");
}

MgByteReader* MgdFeatureService::DescribeWfsFeatureType(MgResourceIdentifier* featureSourceId,
                                             MgStringCollection* featureClasses,
                                             CREFSTRING namespacePrefix,
                                             CREFSTRING namespaceUrl) 
{ 
	NOT_IMPLEMENTED(L"MgdFeatureService::DescribeWfsFeatureType");
}

MgByteReader* MgdFeatureService::GetWfsFeature(MgResourceIdentifier* featureSourceId,
                                    CREFSTRING featureClass,
                                    MgStringCollection* requiredProperties,
                                    CREFSTRING srs,
                                    CREFSTRING filter,
                                    INT32 maxFeatures) 
{ 
	NOT_IMPLEMENTED(L"MgdFeatureService::GetWfsFeature");
}

MgByteReader* MgdFeatureService::GetWfsFeature(MgResourceIdentifier* featureSourceId,
                                    CREFSTRING featureClass,
                                    MgStringCollection* requiredProperties,
                                    CREFSTRING srs,
                                    CREFSTRING filter,
                                    INT32 maxFeatures,
                                    CREFSTRING wfsVersion,
                                    CREFSTRING outputFormat,
                                    CREFSTRING sortCriteria,
                                    CREFSTRING namespacePrefix,
                                    CREFSTRING namespaceUrl) 
{ 
	NOT_IMPLEMENTED(L"MgdFeatureService::GetWfsFeature");
}

MgByteReader* MgdFeatureService::EnumerateDataStores(CREFSTRING providerName, CREFSTRING partialConnString) 
{ 
    if (providerName.empty())
        throw new MgInvalidArgumentException(L"MgdFeatureService::EnumerateDataStores", __LINE__, __WFILE__, NULL, L"", NULL);
    
	FdoPtr<FdoIConnection> conn = MgFdoConnectionUtil::CreateConnection(providerName, partialConnString);
	conn->Open();

	FdoPtr<FdoIListDataStores> list = (FdoIListDataStores*)conn->CreateCommand(FdoCommandType_ListDataStores);
	list->SetIncludeNonFdoEnabledDatastores(true);
	
	FdoPtr<FdoIDataStoreReader> dsReader = list->Execute();

	STRING xml = L"<DataStoreList>\n";

	while (dsReader->ReadNext())
	{
		xml += L"\t<DataStore>\n";
		xml += L"\t\t<Name>";
		xml += dsReader->GetName();
		xml += L"</Name>\n";
		xml += L"\t\t<FdoEnabled>\n";
		xml += dsReader->GetIsFdoEnabled() ? L"true" : L"false";
		xml += L"</FdoEnabled>\n";
		xml += L"\t</DataStore>\n";
	}
	dsReader->Close();

	xml += L"</DataStoreList>";

	Ptr<MgByteReader> reader;
	Ptr<MgByteSource> byteSource = new MgByteSource((unsigned char*)xml.c_str(), (INT32)xml.length());
    byteSource->SetMimeType(MgMimeType::Xml);
	reader = byteSource->GetReader();
	return reader.Detach();
}

MgByteReader* MgdFeatureService::GetSchemaMapping(CREFSTRING providerName, CREFSTRING partialConnString) 
{	
	NOT_IMPLEMENTED(L"MgdFeatureService::GetSchemaMapping");
}

void MgdFeatureService::RegisterProvider(CREFSTRING providerLibraryPath)
{
    MG_FEATURE_SERVICE_TRY()

        FdoPtr<IProviderRegistry> provReg = FdoFeatureAccessManager::GetProviderRegistry();
        FdoPtr<IConnectionManager> connMgr = FdoFeatureAccessManager::GetConnectionManager();

        //As of FDO 3.6 this supports FDO dll paths
        FdoPtr<FdoIConnection> conn = connMgr->CreateConnection(providerLibraryPath.c_str());
        FdoPtr<FdoIConnectionInfo> connInfo = conn->GetConnectionInfo();

        //FDO connections already have this information!
        provReg->RegisterProvider(connInfo->GetProviderName(),
                                  connInfo->GetProviderDisplayName(),
                                  connInfo->GetProviderDescription(),
                                  connInfo->GetProviderVersion(),
                                  connInfo->GetFeatureDataObjectsVersion(),
                                  providerLibraryPath.c_str(),
                                  false);

    MG_FEATURE_SERVICE_CATCH_AND_THROW(L"MgdFeatureService::RegisterProvider")
}

void MgdFeatureService::UnregisterProvider(CREFSTRING providerName)
{
    MG_FEATURE_SERVICE_TRY()

        FdoPtr<IProviderRegistry> reg = FdoFeatureAccessManager::GetProviderRegistry();
        reg->UnregisterProvider(providerName.c_str());

    MG_FEATURE_SERVICE_CATCH_AND_THROW(L"MgdFeatureService::UnregisterProvider")
}

//INTERNAL_API:
//These API's don't need to be implemented as we're not a server process sending data down the wire

//Unfortunately, this is needed by MgdMap, so we have to implement it
MgPropertyDefinitionCollection* MgdFeatureService::GetIdentityProperties(MgResourceIdentifier* resource,
                                                                         CREFSTRING schemaName,
                                                                         CREFSTRING className)

{
    CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::GetIdentityProperties");
    Ptr<MgPropertyDefinitionCollection> propDefs;

    MG_FEATURE_SERVICE_TRY()

    if (className.empty())
    {
        throw new MgClassNotFoundException(
            L"MgdFeatureService::GetIdentityProperties",
            __LINE__, __WFILE__, NULL, L"", NULL);
    }

    MgFeatureServiceCache* cache = MgFeatureServiceCache::GetInstance();
    propDefs = cache->GetClassIdentityProperties(resource, schemaName, className);
    if (NULL == propDefs)
    {
        Ptr<MgClassDefinition> clsDef = cache->GetClassDefinition(resource, schemaName, className);
        if (NULL == clsDef)
        {
            clsDef = GetClassDefinition(resource, schemaName, className);

            //Cache for future calls
            cache->SetClassDefinition(resource, schemaName, className, clsDef);
        }

        propDefs = clsDef->GetIdentityProperties();
        //Cache for future calls
        cache->SetClassIdentityProperties(resource, schemaName, className, propDefs);
    }
    MG_FEATURE_SERVICE_CATCH_AND_THROW(L"MgdFeatureService::GetIdentityProperties")

    return propDefs.Detach();
}

//Unfortunately, this is needed by MgdMap, so we have to implement it
MgClassDefinitionCollection* MgdFeatureService::GetIdentityProperties(MgResourceIdentifier* resource,
                                                                      CREFSTRING schemaName,
                                                                      MgStringCollection* classNames)
{
    CHECK_FEATURE_SOURCE_ARGUMENT(resource, L"MgdFeatureService::GetIdentityProperties");
    Ptr<MgClassDefinitionCollection> classDefs = new MgClassDefinitionCollection();

    MG_FEATURE_SERVICE_TRY()

    if (NULL == classNames || classNames->GetCount() == 0)
    {
        throw new MgClassNotFoundException(
            L"MgdFeatureService::GetIdentityProperties",
            __LINE__, __WFILE__, NULL, L"", NULL);
    }

    MgFeatureServiceCache* cache = MgFeatureServiceCache::GetInstance();
    Ptr<MgStringCollection> uncachedClasses = new MgStringCollection();

    for (INT32 i = 0; i < classNames->GetCount(); i++)
    {
        STRING clsName = classNames->GetItem(i);
        Ptr<MgPropertyDefinitionCollection> propDefs = cache->GetClassIdentityProperties(resource, schemaName, clsName);
        if (NULL == propDefs)
        {
            uncachedClasses->Add(clsName);
        }
        else
        {
            Ptr<MgClassDefinition> clsCopy = new MgClassDefinition();
            clsCopy->SetName(clsName);
            Ptr<MgPropertyDefinitionCollection> idProps = clsCopy->GetIdentityProperties();
            for (INT32 k = 0; k < propDefs->GetCount(); k++)
            {
                Ptr<MgPropertyDefinition> idProp = propDefs->GetItem(k);
                idProps->Add(idProp);
            }
            classDefs->Add(clsCopy);
        }
    }

    if (uncachedClasses->GetCount() > 0)
    {
        Ptr<MgFeatureSchemaCollection> schemas = DescribeSchema(resource, schemaName, uncachedClasses);
        for (INT32 i = 0; i < schemas->GetCount(); i++)
        {
            Ptr<MgFeatureSchema> schema = schemas->GetItem(i);
            Ptr<MgClassDefinitionCollection> classes = schema->GetClasses();

            for (INT32 j = 0; j < classes->GetCount(); j++)
            {
                Ptr<MgClassDefinition> cls = classes->GetItem(j);

                //Cache it
                cache->SetClassDefinition(resource, schema->GetName(), cls->GetName(), cls);

                Ptr<MgClassDefinition> clsCopy = new MgClassDefinition();
                clsCopy->SetName(cls->GetName());
                Ptr<MgPropertyDefinitionCollection> idProps = clsCopy->GetIdentityProperties();
                Ptr<MgPropertyDefinitionCollection> propDefs = cls->GetIdentityProperties();

                //Cache it
                cache->SetClassIdentityProperties(resource, schema->GetName(), cls->GetName(), propDefs);

                for (INT32 k = 0; k < propDefs->GetCount(); k++)
                {
                    Ptr<MgPropertyDefinition> idProp = propDefs->GetItem(k);
                    idProps->Add(idProp);
                }

                classDefs->Add(clsCopy);
            }
        }
    }
    MG_FEATURE_SERVICE_CATCH_AND_THROW(L"MgdFeatureService::GetIdentityProperties")

    return classDefs.Detach();
}

//TODO: This is needed by MgRaster::GetStream()
MgByteReader* MgdFeatureService::GetRaster(CREFSTRING reader, INT32 xSize, INT32 ySize, STRING propName) 
{ 
    NOT_IMPLEMENTED(L"MgdFeatureService::GetRaster"); 
}

// Feature
MgBatchPropertyCollection* MgdFeatureService::GetFeatures(CREFSTRING featureReader) { NOT_IMPLEMENTED(L"MgdFeatureService::GetFeatures"); }
bool MgdFeatureService::CloseFeatureReader(CREFSTRING featureReader) { NOT_IMPLEMENTED(L"MgdFeatureService::CloseFeatureReader"); }

// SQL
MgBatchPropertyCollection* MgdFeatureService::GetSqlRows(CREFSTRING sqlReader) { NOT_IMPLEMENTED(L"MgdFeatureService::GetSqlRows"); }
bool MgdFeatureService::CloseSqlReader(CREFSTRING sqlReader) { NOT_IMPLEMENTED(L"MgdFeatureService::CloseSqlReader"); }

// Data
MgBatchPropertyCollection* MgdFeatureService::GetDataRows(CREFSTRING dataReader) { NOT_IMPLEMENTED(L"MgdFeatureService::GetDataRows"); }
bool MgdFeatureService::CloseDataReader(CREFSTRING dataReader) { NOT_IMPLEMENTED(L"MgdFeatureService::CloseDataReader"); }

STRING MgdFeatureService::GetFdoCacheInfo() { NOT_IMPLEMENTED(L"MgdFeatureService::GetFdoCacheInfo"); }

// Commit the transaction specified by the transaction id.
bool MgdFeatureService::CommitTransaction(CREFSTRING transactionId) { NOT_IMPLEMENTED(L"MgdFeatureService::CommitTransaction"); }

// Rollback the transaction specified by the transaction id.
bool MgdFeatureService::RollbackTransaction(CREFSTRING transactionId) { NOT_IMPLEMENTED(L"MgdFeatureService::RollbackTransaction"); }

MgSqlDataReader* MgdFeatureService::ExecuteSqlQuery(MgResourceIdentifier* resource,
                                         CREFSTRING sqlStatement,
                                         MgParameterCollection* parameters,
                                         MgTransaction* transaction,
                                         INT32 fetchSize) { NOT_IMPLEMENTED(L"MgdFeatureService::ExecuteSqlQuery"); }